Interview guides · Robinhood
Robinhood Software Engineer Interview Guide
Robinhood Software Engineer interview guide: the four reported stages, graph and hashing coding questions, job scheduler design, and a 7-day prep plan.
Interviewing at Robinhood
- Practice bank: 48+ questions
- Rounds: 2
- Typical prep: 1–2 weeks
- Interview reports: 13
Interview rounds
| Round | Questions |
|---|---|
| Technical Screen | 17 |
| Onsite | 31 |
How hard is the Robinhood Software Engineer interview?
| Difficulty | Share | Questions |
|---|---|---|
| Easy | 13% | 6 |
| Medium | 81% | 39 |
| Hard | 6% | 3 |
What Robinhood actually tests for
| Topic | Share | Questions |
|---|---|---|
| Coding & Algorithms | 40% | 19 |
| System Design | 33% | 16 |
| Software Engineering Fundamentals | 15% | 7 |
| Behavioral & Leadership | 13% | 6 |
The 60-second brief
The source notes for this role place Robinhood's engineering work in user-facing financial products: brokerage infrastructure, real-time market data streaming, order execution and internal data platforms. A Software Engineer may sit in backend services written in Go and Python, cloud infrastructure on AWS and Kubernetes, web interfaces in JavaScript and React, or mobile apps in Swift or on Android. Robinhood operates in a regulated financial environment, so be ready to discuss correctness, auditability and operational safety as well as speed. One reported design question is about consistency trade-offs.
Robinhood candidates report 4 rounds · ≈ 3-5 weeks. The stages below are what candidates describe, not a published process.
What a Software Engineer does at Robinhood
The listed responsibilities are designing and operating scalable systems, keeping them reliable through clear alerting and safe deployment controls, working with product managers, data engineers, security specialists and business operations, and reducing operational friction by automating deployment workflows and building shared libraries. The nice-to-have background includes fintech, brokerage or banking infrastructure, and tools such as ArgoCD and Terragrunt. Because Robinhood operates in a regulated financial environment, the notes put code quality, security compliance and test coverage next to speed of execution.
For preparation, the takeaway is that the interview is not one generic coding loop. Candidates report that questions vary by domain and level, and web and mobile candidates describe practical build rounds. Ask your recruiter how the rounds for your track split between algorithms, practical builds and system design, and weight your practice to match. Candidates describe a Project Deep Dive round in the onsite; confirm with your recruiter whether your track has one.
The Robinhood interview process
Robinhood candidates report 4 rounds · ≈ 3-5 weeks. The stages below are what candidates describe, not a published process.
Online Assessment
Candidates describe the first stage as an online assessment or a technical screening call, often run on a third-party platform such as Karat or CodeSignal. The reported coding questions are short, rule-heavy data-processing problems: return the course a student takes halfway through a path built from prerequisite pairs, return the shared courses for every pair of students, credit referrals when a user earns credit only for their first valid referral, and a string encryption problem with a custom key. The reports do not tie these to a specific stage, so treat them as the coding category to drill for any round. The source notes describe the technical interviews as tightly timed. Misread rules and trial-and-error debugging cost time as easily as a wrong algorithm, so pin the rules down before coding. Ask which platform and language setup you will get, and practise in a plain editor that matches it.
What it evaluates
- Whether you turn a short, rule-heavy prompt into explicit rules and edge cases before coding
- Whether you choose a hash map, set or adjacency map from the access pattern and can state the resulting complexity
- Whether the code handles empty, single-element and rule-violating inputs on the first run
How to prepare
- Solve the reported set from a blank file: the halfway course from prerequisite pairs, shared courses for every student pair, and referral credits under a first-valid-referral rule
- For each problem, write three hand-made test cases (empty, minimal, and the one that exercises the tricky rule) before writing the function
- Ask whether the assessment runs on Karat, CodeSignal or another tool, and rehearse in an environment without autocomplete
Recruiter Conversation
Use this conversation to find out which track the role belongs to. Candidates report that questions vary by domain (backend, infrastructure, frontend, mobile) and by level, and the practical rounds are domain-specific. Ask what the technical screening covers, whether the onsite includes a project deep dive with slides, and which language and environment the coding rounds use. Candidates also report that some staff-level tracks replace the initial coding screen with an early architecture round, so if you are interviewing at that level, ask whether that applies to you. Raise hard constraints now: start date, location, work authorisation and any competing deadline. They are much harder to resolve once the onsite is booked.
What it evaluates
- Whether your background, level and constraints fit the role before later rounds are scheduled
- Whether you can summarise your most relevant work in two sentences and connect it to the track
- Whether your timeline and expectations are stated plainly rather than left for the offer stage
How to prepare
- List the questions you need answered: track, screening format, whether the onsite has a project presentation, and the coding environment
- Write each hard constraint in one line and state it as a fact during the call
- Prepare a two-sentence summary of the project you plan to expand into the deep dive later
Technical Screening
The source notes describe this stage as domain-specific live coding or an assessment of core computer science fundamentals. For backend and general tracks, prepare the reported algorithm families: graph traversal and topological ordering, hashing and string processing, and dynamic programming. The reported practical tasks are not tied to a stage, but they show what domain-specific coding looks like here. Web candidates describe building a weekly calendar with an event model, or implementing throttling, batching and an async promise queue, and explaining the JavaScript event loop and rendering pipeline. Mobile candidates describe building a native feature that fetches remote data and renders it in Swift or on Android. Before coding, state your approach and the input bounds that justify its complexity. For a fundamentals question, explain the mechanism rather than reciting a definition.
What it evaluates
- Whether the approach is justified by the input size and the stated rules rather than by pattern recognition alone
- Whether you explain your reasoning aloud while coding and catch your own edge cases
- Whether fundamentals answers show the underlying mechanism, such as how the event loop orders tasks and microtasks
How to prepare
- Drill topological sort, BFS and DFS, hash-map grouping and one memoised dynamic programming problem until you can write each without references
- If you are on the web track, implement a throttle function and a promise queue with a concurrency limit, and explain the event loop in your own words
- If you are on the mobile track, build a small app that fetches a JSON list and renders loading, error and empty states
Virtual Onsite Loop
Candidates report that the virtual onsite combines coding, system design and project-focused interviews, including a Project Deep Dive round where you present a system you built on one or two slides. The reported design questions are a distributed job scheduler that runs user tasks from cron expressions, a real-time messaging, notification or document e-signature system, consistency choices for metadata retrieval, web analytics and bank account checks, a photo album service or distributed counter, and round-robin load balancing with its bottlenecks. The same project can come up in the deep dive and in behavioral questions, so fix its facts in advance and make sure both accounts agree.
What it evaluates
- Whether a design states its guarantees (execution semantics, consistency, durability) before optimizing latency
- Whether the project presentation explains architecture, trade-offs and edge cases rather than product features
- Whether the figures and decisions you give for a project stay the same across interviews
How to prepare
- Design the cron-driven job scheduler twice, once at-most-once and once at-least-once with idempotent jobs, and list what changes between the two
- Build one or two slides for a single project: an architecture slide with the data paths, and a decisions slide that names the alternative you rejected
- Work the worked design exercise on serving balances from a ledger and the idempotency drill to practise financial consistency arguments
- Run one mock with coding, design and a deep dive back to back
Questions Robinhood candidates report
Data structures and algorithms
One structure rarely covers the whole operation set, and the interesting questions in this category are the ones where it does not. When you need constant-time lookup and constant-time reordering at once, or the minimum plus deletion of an arbitrary element, the answer is two structures that index each other: a map from key to the node holding it, beside the list or heap that maintains the order. What separates a strong answer is the invariant between them, stated explicitly. Every operation has to leave both halves consistent, and the bug people ship is a removal from one that leaves a stale entry in the other.
- Given a list of (student ID, course name) pairs, write a function that returns all shared courses for every pair of students.
- Frontend / Web: Build or extend application features in vanilla JavaScript or React (e.g., building a interactive weekly calendar with event models, or implementing custom throttling, batching, and async promise queues).
- Mobile (iOS / Android): Build a functional native application from scratch or within a starter environment that fetches remote network data and renders dynamic, adaptive UI components using Swift or Android Studio.
- Given a list of prerequisite and course pairs representing a single learning path, return the course that a student takes when they are halfway through their program.
System design and architecture
A design is a set of promises about what happens when something is slow, down, or delivered twice, and that is the part usually left unsaid. For each dependency, say what the system does when it is unavailable: serve stale data, reject the write, or accept it and settle later. For each relaxation of consistency, describe the anomaly a user would actually notice, in the product's own words, rather than naming a consistency model and moving on. The separator is volunteering the failure behaviour as part of the design instead of producing it only when pushed.
- Design a high-scale photo album application or distributed counter, covering data normalization, state management, sorting, and API layout.
- Evaluate consistency models: determine when to apply strong consistency versus eventual consistency for low-latency metadata retrieval, high-volume web analytics, and bank account checking operations.
Behavioural and engineering judgement
Every question in this category has a number in it somewhere, and engineers routinely tell the story without one. What the outage cost, what the delay cost, what the rewrite consumed in engineer-weeks against what it saved, and who absorbed it when it was not users: the on-call rotation, another team's quarter, the support queue. You will rarely have an exact figure, and an estimate with its method stated beats both a precise-sounding invention and silence. Say how you got to it and what it leaves out. That is the sentence that makes the follow-up about whether it was worth doing answerable at all.
- Describe a situation where you resolved technical debt or settled architectural disagreements within an engineering team.
- Explain how you handle production outages, high-stress bug fixes, and cross-functional alignment with product and operations teams.
Practice exercises with worked solutions
The prompts above tell you what gets asked.
Version a loan schedule instead of soft-deleting posted instalments
loan_instalment is keyed by (loan_id, schedule_version, instalment_no) and carries due_date, principal_minor, interest_minor, fee_minor, paid_principal_minor, paid_interest_minor, status, days_past_due, effective_from (date) and superseded_at (timestamptz). A borrower defers two payments on 2026-03-14, and instalments 1 to 6 already have allocations posted against them. Model exactly what the deferral writes, and write the query that returns the schedule as the borrower saw it on an arbitrary date. Say why stamping the old rows with deleted_at, or updating them in place, fails an audit.
How a strong candidate works it
- Treat the deferral as an insert, not an edit: write a complete new schedule_version with effective_from = the deferral instant on 2026-03-14, and in the same transaction stamp superseded_at on every row of the outgoing version with that identical instant, so the two version windows are half-open and adjacent rather than overlapping. Instalments 1 to 6 are reproduced unchanged, because they are what the borrower was told and what was posted to the ledger.
- Write the as-of read as a version selection, not a row filter: WHERE loan_id = $1 AND effective_from <= $2 AND (superseded_at IS NULL OR superseded_at > $2), then assert that exactly one schedule_version comes back - COUNT(DISTINCT schedule_version) = 1, not one row - so an overlapping window raises rather than silently returning two interleaved schedules under one instalment_no.
- Fix the type mismatch before writing that predicate: effective_from is declared date and superseded_at timestamptz, so comparing them casts the date at the session TimeZone and two readers in different zones select different versions near midnight. Put both columns in one domain, timestamptz, keep the window half-open with effective_from inclusive and superseded_at exclusive, and resolve a bare as-of date to an instant once, in the loan's booking timezone, at the edge of the system.
- Say what deleted_at loses. It records that a row stopped being current but not what replaced it or from when, it leaves every downstream query obliged to remember deleted_at IS NULL, and one query that forgets double-counts the schedule. Versioning puts the same information in the primary key where it cannot be forgotten.
- Close the arithmetic: under the loan's stated day-count convention the new version must still sum to outstanding principal plus scheduled interest to the minor unit, with the per-period rounding residual placed in one named instalment, conventionally the last, rather than smeared across the tail.
- Index (loan_id, effective_from DESC) for the as-of lookup, keep superseded versions online rather than archiving them, and enforce with a trigger that no UPDATE touches a row whose paid_principal_minor or paid_interest_minor is non-zero.
Expected result (25 min): Two schedule_versions for the loan. The as-of query returns version 1 for 2026-03-01 and version 2 for 2026-03-20, instalments 1 to 6 are identical across both versions, and SUM(principal_minor) equals the original principal exactly in each version.
Check yourself
- The live-version invariant is per version, not per row: SELECT COUNT(DISTINCT schedule_version) FROM loan_instalment WHERE loan_id = $1 AND superseded_at IS NULL returns 1, while COUNT(*) over the same predicate returns that version's instalment count - 12 here - so an assertion of one NULL row per loan would fail on the correct design.
- Run the as-of query for 200 random dates spanning the loan and assert it returns exactly one schedule_version every time.
- Run the as-of query at exactly the deferral instant and assert version 2 alone: the windows are half-open and version 1's superseded_at equals version 2's effective_from, so the boundary belongs to exactly one version.
- Attempt an UPDATE on an instalment with paid_principal_minor > 0 and confirm the trigger rejects it.
- Change the session TimeZone and re-run the boundary instant; with both columns timestamptz the selected version must not change, which is the test that the date column was actually retyped.
Follow-ups
- What does days_past_due mean for an instalment that exists in two versions with different due_dates?
- A payment arrives allocated to an instalment_no that exists only in the superseded version. What do you do with it?
- How do you prove the ledger postings made under version 1 still reconcile once version 2 exists?
Make a charge endpoint safe under concurrent duplicate retries
idempotency_key holds id, scope, key, request_fingerprint (SHA-256 over the canonicalised body), status (in_progress, completed, failed), response_status, response_body, locked_at, completed_at, expires_at, created_at. Fifty identical create-payment requests carrying the same scope and key reach four application instances inside the same 20 ms. Give the DDL constraint and the exact statements the handler runs so that exactly one payment_intent is created and all fifty callers receive the same response body. State what you return when that key arrives with a different fingerprint, and what an arrival after expires_at means.
How a strong candidate works it
- Put the concurrency control in the schema: UNIQUE (scope, key). A SELECT-then-INSERT cannot work because both transactions can read nothing before either commits, so the check passes twice and the constraint then surfaces as an error on a payment that succeeded.
- Claim the key with INSERT ... ON CONFLICT (scope, key) DO NOTHING RETURNING id. A conflict returns zero rows rather than the existing row, so branch on rowcount: the winner proceeds, the loser reads the stored row.
- Keep that path on READ COMMITTED deliberately. The loser's follow-up SELECT takes a fresh statement snapshot and therefore sees the winner's committed row; under REPEATABLE READ the transaction snapshot predates that commit, the row stays invisible and the loser concludes the key does not exist.
- Split the work across two transactions because the processor call cannot sit inside one: commit the in_progress row with locked_at first so losers can see a claim, perform the effect, then write payment_intent plus status=completed with response_status and response_body in a single second transaction.
- Handle the crash window explicitly: a row stuck in_progress past its lease is an unknown outcome, not a failure, so the reaper queries the processor for that key before deciding. A loser that sees in_progress returns 409 and retries rather than repeating the effect.
- Compare request_fingerprint before replaying anything. Same key with a different body is 409, never the cached response, because replaying confirms a payment the caller did not request; and set expires_at beyond the client's and the processor's maximum retry horizon, since a replay after it is a genuinely new request.
Follow-ups
- The handler dies after the processor call and before the local commit. What does the next retry with that key observe, and how does the system converge on exactly one charge?
- Does the downstream processor honour an idempotency key of its own? Who mints it, and what breaks if a fresh one is generated per attempt?
- How do you purge rows past expires_at without the delete contending with the insert path?
Derive per-account balances and catch unbalanced transactions
You are given ledger_entry rows streamed in entry_id order: transaction_id, account_id, direction (debit or credit), amount_minor (a positive int64), currency, business_date. Up to 500 million rows, at most 20 million distinct (account_id, currency) pairs, and the entries of one transaction are contiguous in the stream. In a single pass with no re-reads, return the closing balance per (account_id, currency) and the transaction_id of every transaction whose entries do not sum to zero within each currency. State your time and space bounds.
How a strong candidate works it
- Normalise the sign at read time from
direction, not from the amount:signed = +amount_minorfor debit,-amount_minorfor credit (state which convention you picked). The schema constrainsamount_minor > 0precisely so the sign lives in exactly one place. - Hold one hash map keyed
(account_id, currency)to an int64 running total. Twenty million keys at 16 bytes of payload plus map overhead is order 1 GB in most runtimes — quote the number, and offer the fallback: partition the stream byhash(account_id) % Pand run P passes for 1/P of the memory. - Ride the zero-sum check on the same pass. Because a transaction's entries are contiguous, keep a tiny
currency -> int64map for the currenttransaction_idonly, test it against zero on the boundary and at EOF, then clear it. That is O(currencies in one transaction), typically one or two. - Bound the arithmetic explicitly. Int64 holds about 9.22e18, so overflowing one account across 500 million entries needs an average of 1.8e10 minor units per entry — safe here, but use a checked add so an adversarial file fails loudly rather than wrapping.
- Complexity: O(n) time, O(distinct account-currency pairs) space, one sequential pass, no sort. The zero-sum check adds no asymptotic cost, which is the argument for doing it here rather than in a second job.
Expected result (20 min): The balances map is identical under any within-transaction ordering; exactly the third transaction is reported; the mixed-currency transaction is accepted, because zero-sum is required per currency, not across the transaction.
Check yourself
- Across the whole file, the sum of every account balance is exactly 0 per currency whenever no transaction is flagged.
- Two runs over the same file produce byte-identical output.
- Swapping the int64 accumulator for binary64 makes a fixture with amounts above 2^53 minor units disagree — that is a failing test, not a review comment.
Follow-ups
- Entries of a transaction are no longer contiguous. What does the zero-sum check cost now, and which is cheaper: buffering open transactions or an external sort on
transaction_id? - How would you produce the same balances as of an arbitrary
business_datewithout a second full scan? - The job is restarted after a crash halfway through the file. What makes the second run produce identical output?
Serve balances and statements without re-summing the ledger
Balances are derived from an append-only ledger_entry table: entry_id, transaction_id, account_id, direction (debit|credit), amount_minor, currency, source_type, source_id, business_date, posted_at. Reads run at 50,000/s from customer apps and merchant dashboards; postings run at 10,000 entries/s. Large accounts hold tens of millions of entries, so summing per read is not viable. Design the read path: where the current balance lives, when it is written relative to the entries, what a caller sees immediately after its own posting, and how a statement for a closed business date stays byte-identical on every future read. State your invalidation rule.
How a strong candidate works it
- Split the request into two reads with different requirements rather than one 'balance API'. Current balance is a single row that must be read-your-writes for the party who just posted. A statement is a bounded range over entries for one business_date window, and once that date's cutoff has passed the range is immutable, so it is cacheable indefinitely rather than for a guessed TTL.
- Write the materialised balance inside the same database transaction as the entries: UPDATE account_balance SET amount_minor = amount_minor + $delta, version = version + 1 WHERE account_id = $1, alongside the INSERTs. An asynchronous updater fed from the entry stream is the tempting alternative and is wrong here, because it makes the balance disagree with the system of record for exactly as long as the consumer lags, which is exactly when someone is refreshing the screen.
- Serve the balance as a primary-key lookup, O(1) and sub-millisecond, and serve the statement as an index range scan on (account_id, business_date, entry_id), O(k) in rows returned rather than O(n) in account history.
- Invalidate by writing through on commit, keyed by account_id and carrying the version, not by TTL. A TTL on a balance is a defined interval during which you knowingly display a stale number; a version lets a reader detect staleness instead of hoping.
- Price the design honestly: the extra balance UPDATE serialises postings on that account row, and that lock hold is what sets the per-account write ceiling. Measure it before deciding the read path is free.
Expected result (20 min): The full-history SUM costs hundreds of milliseconds to seconds and grows with history; the single-date SUM is bounded by the index range; the balance lookup is sub-millisecond and constant in account size. The second session sees the old balance until the posting commits and the new one immediately after, never a state where entries exist without the balance move. The closed-date statement is identical on both runs.
Check yourself
- SUM of entries signed by direction equals the materialised balance for every account, asserted on a schedule rather than once.
- The application role holds no UPDATE or DELETE privilege on ledger_entry.
- A backdated posting into an already-closed business date either fails or changes that date's statement; decide which, and have a test that proves which one happens.
Follow-ups
- A customer disputes a balance shown three months ago. Reproduce that exact number from the data you kept.
- The materialised balance and the sum of entries differ by one minor unit. How do you find that before the customer does, and what do you do about it?
- What changes if the balance must also be readable from a second region with a 70 ms round trip?
One merchant stops receiving webhooks while the rest deliver
The outbound relay delivers outbox_event rows to merchant endpoints preserving per-destination ordering. One merchant has received nothing since 09:14; every other destination is current. The relay process is healthy and CPU is flat. For that merchant, unpublished rows are accumulating, the oldest has attempts = 412 with backoff capped at 30 seconds, and last_error is the same string on every attempt. Diagnose in order, and state what you change so one undeliverable row can never stall a destination again.
How a strong candidate works it
- Separate destination down from one row undeliverable before anything else, because they are identical from the merchant's side. The discriminator is the error class: a transport error (connection reset, 503, TLS failure) varies and implicates the endpoint, while an identical deterministic error on every one of 412 attempts implicates the row.
- Read the blocked set in version order: SELECT event_id, aggregate_id, aggregate_version, attempts, last_error FROM outbox_event WHERE published_at IS NULL AND aggregate_id = $1 ORDER BY aggregate_version LIMIT 5. If everything is queued contiguously behind one row, the ordering guarantee is working exactly as designed and the defect is that it has no exit.
- Confirm outside the relay: replay that single payload against the destination by hand. Reproducing the identical error proves the row, not the transport, and stops the escalation to the merchant before it is sent.
- Decide what skipping means under a per-destination ordering contract. You cannot simply publish the next row; you either park the poison row in a dead-letter table and accept that the consumer sees an aggregate_version gap, or you deliver a degraded payload. Name which, and say what the consumer does with the gap.
- Fix the real defect, which is unbounded retry, not the malformed payload. Bound retries by attempt count or row age, park on exhaustion, alert on the age of the oldest unpublished row per destination, and validate the payload at INSERT time so it cannot be written inside the state-change transaction at all.
Follow-ups
- The consumer orders on aggregate_version. What does it do with the gap your dead-letter created, and how does it learn the gap is intentional?
- How do you distinguish this from a destination returning 4xx on every request during its own deploy?
- Does the alert go on attempts, on the age of the oldest unpublished row, or both, and what are the thresholds?
Resolve a review disagreement over isolation level
A colleague's pull request reads a balance, compares it to a floor in application code, then issues an UPDATE with the computed value, all under PostgreSQL's default READ COMMITTED. You comment; they reply that staging has never produced a negative balance. Describe a code review disagreement where you were confident and the author was not convinced. State how you made the failure concrete rather than theoretical, how many rounds it took, at what point you would have escalated or approved anyway, and what you conceded to the author.
How a strong candidate works it
- The probe is whether you can convert a correctness objection into something reproducible instead of a stalemate of opinions. Name the anomaly by its mechanism: under READ COMMITTED each statement takes a fresh snapshot, so two sessions can both read balance 100, both compute 100 minus 80, and both write 20.
- Address the staging evidence directly rather than dismissing it. Staging concurrency on one account row is effectively one, so the absence of the anomaly there is expected under both the broken and the correct implementation. That is the sentence that usually ends the argument.
- Reproduce it in two psql sessions and paste the interleaving into the review. A twelve-line transcript settles in one round what three paragraphs of theory will not settle in four.
- Offer the fix as a choice with its trade-off, not as a verdict: an atomic UPDATE ... SET balance = balance - $1 WHERE account_id = $2 AND balance - $1 >= $3 with a rowcount check keeps it single statement and needs no retry; SELECT ... FOR UPDATE serialises the row and lets you compute in application code; SERIALIZABLE covers the multi-row version of the predicate but requires a bounded retry on SQLSTATE 40001 that someone has to actually write.
- Say where your bar is. Correctness on money is a blocking comment, style is not, and a strong answer states that boundary before the disagreement rather than discovering it during one.
- Name what you conceded. The author was usually right about something (scope, naming, the follow-up being separable), and saying so is what makes the blocking comment land next time.
Follow-ups
- The author switches the service to MySQL. Which of the three fixes still behaves the same, and which changes silently?
- The same endpoint later transfers between two accounts. What do you now require in the review?
- How do you keep this from being relitigated in every future pull request?
Where candidates lose points
Coding the referral or course prompt before pinning down its rules
The reported coding questions are short to state and full of rules: referral pairs where a user can refer many people but earns credit only for a first valid referral, or prerequisite pairs that form one path whose halfway course you return. Before typing, ask and write down what makes a referral invalid (self-referral, a referee who already appeared, a cycle), whether duplicates occur, and which course counts as halfway when the path length is even. For the path, find the one course that is never a successor and walk a next-course map in O(n). A general topological sort is more than this needs. Testing with an empty input, a single pair and an even-length path catches most misses.
Designing the job scheduler without first stating its execution guarantee
Say at the start whether a job may run twice, may be skipped, or neither. At-most-once needs a durable claim recorded before execution and accepts that a crashed worker's run is lost. At-least-once needs lease expiry plus idempotent job bodies. Exactly-once in practice means at-least-once delivery with an idempotent effect. Then cover how the next fire time is computed from the cron expression, how due jobs are found through an index on next run time instead of a table scan, and what happens when a worker or the dispatcher dies.
Giving one consistency answer for all three workloads in the consistency question
The reported prompt names metadata retrieval, high-volume web analytics and bank account checking because each needs a different answer. Classify them separately and name the anomaly you accept in each. Analytics can be eventually consistent because a late event changes a count, not a balance. Metadata reads can often be served slightly stale if each record carries a version the client can check. A balance check that gates a withdrawal needs a linearizable read or a conditional write, because a stale read lets two debits both pass. Cover durability and transactional guarantees on the money path before latency, then state what the strong choice costs you.
Filling the project deep dive slides with product features instead of engineering decisions
The source notes describe a one- or two-slide walkthrough of a project you led, covering architecture, trade-offs, edge cases and the reasons behind your design decisions. Use one slide for the architecture with its data paths and failure points, and one for the three decisions you would defend, each with the alternative you rejected and why. Separate what you personally built from what the team built. Write down the scale, team size and incidents you will quote, and use the same figures whenever the project comes up in another interview.
Drilling only algorithm problems when your track has a practical build round
Candidates report that questions vary by domain. Web candidates describe building or extending features in vanilla JavaScript or React, such as a weekly calendar with an event model, throttling, batching or an async promise queue. Mobile candidates describe building a native app that fetches remote data and renders adaptive UI in Swift or on Android. In the recruiter conversation, ask which track the role is in and what environment the practical round uses. Then rehearse in that setup, including network errors, loading states and re-render behaviour, not only algorithm drills.
Algorithms first, for a candidate whose problem-solving has gone rusty
For someone who has spent the last few years shipping features and reading other people's code, and who has not solved a timed problem from a blank file in a long time. Five days rebuild the primitives and the patterns that sit on them, working from invariants rather than remembered solutions, and the last two attach that back to the rest of the loop.
Day 1 — Graphs and ordering: the prerequisite family
- Solve the reported halfway-course question: build a next-course map and a set of courses that appear as successors, find the single start course, walk the path and return the middle one. Decide and state how an even-length path resolves.
- Generalize to an arbitrary prerequisite graph: topological sort by in-degree, and detect a cycle by checking whether fewer nodes come out than went in.
- Take one DAG counting variant from the bank topics (trigger counts, load factors or unique dependents). Write a test where one node is reachable by two paths, which catches double counting when children's totals are simply summed.
Deliverable: Working solutions for the halfway course and a general topological order, plus one DAG counting variant with a test for a node reachable by two paths.
Day 2 — Hash maps under business rules: referrals and shared courses
- Solve the reported shared-courses question: group enrollments into a set per student, then intersect the sets for every unordered pair of students. State the cost, and when an inverted index from course to students is cheaper.
- Solve the reported referral question: process pairs in order, credit a user only for their first valid referral, and write down each rule for what makes a referral invalid before coding.
- Extend it to a referral leaderboard that returns the top three referrers from chronological events, with an explicit tie-break rule and a test for ties.
Deliverable: Three solutions, each with its written rules and at least three edge-case tests, including one for duplicate pairs.
Day 3 — Money-shaped coding and an online assessment dry run
- Work the worked coding exercise on deriving per-account balances and catching unbalanced transactions. Keep amounts as integer minor units and check the zero-sum rule per currency.
- Practise the bank topics on balances and fractional-share inventory: represent fractional quantities as scaled integers, and write the test that fails if you switch to floating point.
- Do a timed dry run of three reported-style problems in a plain editor without autocomplete, then tally which failures came from a misread rule and which from a bug.
Deliverable: The balances exercise passing its checks, and a dry-run tally that separates rule misreads from coding errors.
Day 4 — System design: the distributed job scheduler
- Write the requirements for the reported scheduler: user-defined tasks, cron expressions, the execution guarantee, retries, and the logs a user can inspect.
- Design the data model with a next-run-time index, a claim step using a lease or conditional update, and the calculation of the next fire time after each run.
- Build a failure table covering a worker crash mid-run, a dispatcher crash, a duplicate claim and a slow job, and state what the system does in each case.
- Redo the design at-most-once, then at-least-once with idempotent jobs, and list every component that changes.
Deliverable: A one-page scheduler design with its stated guarantee, data model and failure table, in two variants.
Day 5 — Consistency, ledgers and idempotency
- Answer the reported consistency question as a three-row table (metadata, web analytics, account checks), with the consistency level, the anomaly you accept and its cost in each row.
- Work the worked design exercise on serving balances and statements without re-summing the ledger, and state your invalidation rule out loud.
- Work the worked SQL exercise on versioning a loan schedule, then the idempotency-key drill for concurrent duplicate charges.
- Sketch a payment processing flow with idempotency and reconciliation, one of the bank's design topics, naming where the idempotency key is minted.
Deliverable: The consistency table, both worked exercises checked against their stated checks, and a payment flow sketch.
Day 6 — Your track's practical round and the remaining design prompts
- Web track: build a weekly calendar that handles overlapping events, implement a throttle and a promise queue with a concurrency limit, and explain the event loop in your own words.
- Mobile track: build a small native screen that fetches a JSON list and renders loading, error and empty states in Swift or on Android.
- Backend or infrastructure track: design a photo album service or distributed counter, and compare round-robin, least-connections and consistent-hashing load balancing.
- All tracks: work the debugging drill on one merchant whose webhooks stopped, and diagnose it in order before changing anything.
Deliverable: One working practical build or design for your track, plus a written diagnosis of the webhook drill.
Day 7 — Project deep dive and a full mock onsite
- Build one or two slides for your strongest project: architecture and data paths, then the key decisions, each with its rejected alternative and the edge cases you handled.
- Write a one-page fact sheet for the project (scale, team size, your part, what broke) and say it aloud until the figures come out the same every time.
- Prepare the reported behavioral prompts: resolving technical debt or an architectural disagreement, and handling a production outage with product and operations partners.
- Run a mock with one coding problem, the scheduler design and the deep dive back to back, then note where your project facts or trade-offs changed.
Deliverable: Deep-dive slides, a project fact sheet, and notes from one full mock covering coding, design and the deep dive.