Turo runs a peer-to-peer car-sharing marketplace that connects local car hosts with guests who book their vehicles. Candidate-reported material for the Software Engineer role describes work across search and discovery, booking flows, payment processing, and trust and safety. It names problems such as real-time availability, pricing, geo-spatial search and high-concurrency booking transactions, and mentions React and Next.js for web work.
The reported questions follow the same domain. They include a SQL query for booking metrics on host vehicles over a time range, an algorithm that finds available time slots in car availability data, an in-memory cache with eviction, API design for a Turo feature, a real-time notification system that alerts hosts of new booking requests, a browser-to-database trace of a booking request, and scaling a read-heavy service. The reported question list also includes mobile and JavaScript topics such as Retrofit, iOS storyboards and an async array prototype. Ask your recruiter whether your team is backend, web or mobile.
These sources are candidate reports, not a published process. Use them to set priorities rather than to predict exact questions. Get comfortable with booking-shaped data (time ranges, overlaps, nulls across joins), with API contracts including status codes and error handling, and with explaining where caching and replication are safe in a booking flow and where they are not.
Recruiter Screen
reportedThe first conversation is with a recruiter about your background and the role. Prepare two things for it. First, an account of your recent work that a non-engineer can repeat accurately to a hiring manager. Second, a specific answer to why Turo. 'Why do you want to work at Turo, and how do you personally relate to its peer-to-peer marketplace model?' appears among the reported behavioral questions, so have it ready before this call. Use the call to find out which team you are interviewing for, whether the work leans backend, web or mobile, whether a take-home is part of the process, and whether the technical screen will be coding or API design. Some candidates report administrative delays and scheduling problems, so confirm the next step and when to expect it before the call ends.
What to demonstrate
- Whether your recent projects come across clearly enough for a non-engineer to summarize them accurately for the hiring manager
- Whether your interest in Turo is specific: a product flow you have used or studied rather than a general statement about marketplaces
- Whether your background and preferred stack fit the team and level being hired for
How to prepare
- Write two-sentence summaries of your top projects with no internal system names: what was breaking, what you changed, what happened after. Have someone outside engineering repeat them back
- Use the Turo app as a guest, read how the host side works, and build your 'why Turo' answer on one specific flow you saw
- Bring questions: which team, backend vs web vs mobile, whether there is a take-home, and whether the technical screen is coding or API design
Technical Screen
reportedCandidates describe this round as focused on coding or API design. Ask your recruiter which one you will get, and prepare both if they cannot say. The reports do not assign the reported coding questions to a particular round. They include array and string manipulation, an in-memory cache with eviction written as fully working, testable code, finding available time slots in car availability data, and an interval problem. The reports stress clean, readable code over algorithmic tricks. So write a correct simple version first, state its complexity, then improve it. Trace it against empty input, a single element, and touching or nested intervals before you say you are finished. If you get API design, cover endpoints, HTTP methods, request bodies, response payloads and status codes. Then cover the error cases and what happens when a client retries.
What to demonstrate
- Whether the code runs and is correct on inputs you were not shown: empty input, one element, duplicates, and intervals that touch or nest
- Whether the code is readable and testable, with clear names, small functions and test cases you can state without prompting
- If the screen is API design, whether the contract covers methods, payloads, status codes and error cases rather than endpoint paths alone
How to prepare
- Implement an LRU cache with O(1) get and put, then write the capacity-boundary tests before calling it finished
- Solve an availability-slot problem: given bookings and a search window, return the free slots using half-open ranges, and test a booking that covers the whole window
- Sketch a booking API on paper with resources, endpoints, methods, bodies, status codes, an error body, and an idempotency key for creating a booking
Multi-Round Virtual Onsite
reportedCandidates describe the onsite as multiple virtual interviews that assess technical and cultural fit. The reports do not say which interview covers what, so prepare every reported category. That means coding. It means SQL, such as booking metrics for specific host vehicles over a time range with null handling and multi-table joins. It means API design and relational data modeling. It means system design: a browser-to-database trace of a booking request, real-time host notifications, and scaling a read-heavy service with caching, message queues and replication. Candidate reports also mention a system design round for mid-to-senior candidates. Behavioral questions cover ambiguous deadlines, disagreements with a product manager and production debugging. Switch modes deliberately between interviews. In coding, finish and test. In design, agree on requirements and on what may be stale before drawing components. In behavioral, name the person, the decision and the result.
What to demonstrate
- Whether SQL answers keep entities with no matching rows, handle nulls explicitly and define the time-range boundaries
- Whether design answers start from requirements (what must be fresh, what may be stale, what must never be applied twice) before naming components
- Whether behavioral answers name a specific disagreement or incident, what you did about it, and what changed afterwards
- Whether you tie technical choices back to what the guest or host experiences
How to prepare
- Write the booking-metrics query against a small dataset that includes a vehicle with no bookings, a cancelled booking and a booking that straddles the range edge
- Trace a booking request from browser to database aloud, naming where caching is safe and where a replica read could allow a double booking
- Run three mocks back to back (coding, design, behavioral) and note where you carried the wrong mode into the next one
1 candidate reports. Individual accounts describe a particular role and hiring cycle.
Turo Software Engineer Interview Experience — Four Onsite Rounds, Verbal Offer in Under a Month
Background I got a referral, and as soon as the role opened they scheduled a call directly. (My personal takeaway: right now applying early matters more than being strong.) Timeline: 01/13 Applied 01/15 Recruiter call 01/15 OA 01/28-01/29 Four rounds of virtual onsite 02/06 Verbal offer Interview Experience Recruiter call Simple questions about my background, how long I've been working, what this…
Read full experiencePracHub editorial advice for the preparation topics above.
Writing the booking-metrics SQL with an INNER JOIN and losing vehicles that had no bookings
The reported SQL prompt asks for booking metrics on specific host vehicles over a time range, with null handling across joins. Start from the vehicle table and LEFT JOIN bookings. Put the date-range and status filters in the ON clause, because a filter on the bookings table in WHERE silently turns the LEFT JOIN back into an inner join. Use COUNT(b.booking_id), not COUNT(*), so a vehicle with no bookings shows 0 instead of 1, and wrap sums in COALESCE. State whether the range is half-open [start, end) so a booking that ends exactly on the boundary is counted once. Say these choices out loud before you run anything.
Answering an API design prompt with a list of endpoint paths and nothing else
Candidates describe the technical screen as coding or API design, and the reported API prompts ask for endpoints, HTTP methods, request bodies, response payloads and status codes. Cover all of them. Then add what separates a usable API from a sketch: the error body, which status each failure returns (for example 400 for malformed input and 409 for a date conflict), how a client retries creating a booking without creating two (an idempotency key), and the booking's status lifecycle. Agree on the resource model (vehicle, availability, booking) before you write any URL.
Checking availability conflicts only against the previous interval, or with closed boundaries
Reported coding prompts include finding available time slots in car availability data and an interval problem. Sort by start and carry the running maximum end rather than comparing each interval with its neighbour. The neighbour comparison misses a short booking nested inside an earlier long one. Use half-open ranges so a return at 10:00 and a pickup at 10:00 do not conflict. Before you say you are done, test empty input, a single booking, touching bookings and a booking that covers the whole search window. The overlapping-reservations worked exercise walks through the nested and touching cases.
Building the in-memory cache without a stated eviction policy or any tests
The reported cache prompt asks for fully working, testable code for eviction, so the tests are part of the answer. Name the policy first (LRU unless you are told otherwise). Pair a hash map with a doubly linked list, or use an insertion-ordered map (for example LinkedHashMap or OrderedDict), so get and put are O(1). A key-sorted map such as TreeMap or std::map does not track recency and costs O(log n). Write the tests that catch eviction bugs. Fill to exactly capacity and assert nothing was evicted. Add one more and assert the least recently used key is gone. Read an old key before that insert and show that the victim changes. Also cover capacity zero and updating an existing key, which should refresh recency without growing the size.
Reciting a generic architecture diagram for the request-flow and read-scaling questions
Reported design prompts include tracing a car-booking request from browser to database, a real-time notification system for hosts, and scaling a read-heavy service with caching, queues and replication. A stock diagram of DNS, load balancer, cache and database does not answer them. Say which data may be stale (search results, listing details) and which may not (the availability check at the moment of booking). Say where a replica read could allow a double booking, and what happens to a host notification when delivery fails: queued, retried, deduplicated. Tie each component to the failure it prevents.
Choose a category, try a prompt, then open its approach, worked solution or follow-up when you need it.
Solve array and string manipulation problems focused on clean, readabl…
Solve array and string manipulation problems focused on clean, readable code and optimal space/time complexity.
Approach
- State the target complexity and say which constraint rules the naive version out.
- Choose the data structure from the access pattern, not from familiarity.
- Walk one small example through your approach before writing the whole thing.
Follow-up
- What is the worst case, and how likely is it on real data?
- How does this change if the input no longer fits in memory?
Design an in-memory cache and write fully working, testable code to ma…
Design an in-memory cache and write fully working, testable code to manage cache eviction.
Approach
- Restate the input: its shape, its size, and what is guaranteed about it.
- Walk one small example through your approach before writing the whole thing.
- Name the brute-force solution and its complexity before improving on it.
Follow-up
- Which test case would catch an off-by-one here?
- How does this change if the input no longer fits in memory?
Find overlapping reservations and the largest free gap
You have up to 2,000,000 booking rows with booking_id, listing_id, status, and reserved_during stored as a half-open UTC range [start, end). Ignoring rows whose status is 'cancelled', report every pair of overlapping reservations on the same listing, and for each listing return the largest free gap inside a supplied horizon. The ranges were materialised from local wall-clock check-in and check-out times in the listing's own timezone. Target O(n log n), and state the exact overlap predicate you use.
Approach
- Bucket rows by listing_id in a hash map first. Overlap is only ever possible within a listing, so you sort many small groups instead of one large one; the bound stays O(n log n) but the constant falls and the work parallelises per listing.
- Within a listing, sort by start ascending with end descending as the tiebreak, then sweep carrying running_max_end. Report a conflict when current.start < running_max_end. Comparing only against the immediately preceding end is the common wrong version: [1,10), [2,3), [4,5) passes that check even though [4,5) is nested inside [1,10).
- State the predicate for half-open ranges explicitly: a and b overlap iff a.start < b.end AND b.start < a.end. Ranges that touch, where a.end == b.start, do not overlap — a same-day turnover is legal. PostgreSQL's && on a tstzrange with the default [) bounds is exactly this predicate, which is why the EXCLUDE constraint in the schema and this offline check agree.
- Compute free gaps from the same sweep: merge while start <= running_max_end, then take max(next.start - prev.end) over consecutive merged ranges, including the leading gap from the horizon start and the trailing gap to the horizon end, clipped to the horizon.
- Bound the output. Reporting all overlapping pairs is O(n log n + p) and p is quadratic on a pathological listing, so either cap p per listing or report only the first conflict per listing when the caller just needs a repair signal.
- Handle the timezone precondition: a local calendar day is 23 or 25 hours across a DST transition, so reserved_during must be materialised at write time by converting local wall-clock times in the listing's zone to UTC instants. Reconstructing it later from a stored local date plus a fixed 24-hour length silently shifts one night per year in each direction.
Worked solution 25 min
- Take one listing with four confirmed ranges in day units — A=[1,5), B=[5,8), C=[3,4), D=[9,11) — plus one cancelled range E=[2,12), over a horizon of [0,14).
- Sort the non-cancelled ranges by start: A[1,5), C[3,4), B[5,8), D[9,11).
- Sweep with running_max_end: after A it is 5; C.start=3 < 5 so report (A,C); B.start=5 is not < 5 so no conflict and max_end becomes 8; D.start=9 is not < 8 so no conflict and max_end becomes 11.
- Merge the non-cancelled ranges into [1,8) and [9,11), then measure the gaps inside [0,14): [0,1) is 1 unit, [8,9) is 1 unit, [11,14) is 3 units.
- Re-run with E included to see how much the status filter changes the answer.
Follow-up
- This audit found conflicts that a live EXCLUDE constraint on (listing_id WITH =, reserved_during WITH &&) should have made impossible — what could have produced them?
- How do you run this incrementally over only the bookings written since the last pass without missing a conflict with an older row?
- One listing has 40,000 reservations and the pair count explodes — what do you return to the caller instead?
Resolve accept-versus-expire races with a guarded conditional update
Two dispatch partitions each read provider_presence.status = 'idle' for provider 88 from an asynchronous replica and each try to offer them a request. At the same moment the TTL sweeper is expiring offer 4471 while that provider taps accept. The database is PostgreSQL at the default READ COMMITTED. Write the exact statement that accepts an offer, the statement the sweeper runs, and the index DDL that makes a second concurrent live offer to provider 88 impossible. For every affected-row count your accept can return, say what the caller does.
Approach
- Make the accept a single guarded write rather than a read followed by a write:
UPDATE dispatch_offer SET status = 'accepted', responded_at = now() WHERE offer_id = $1 AND status = 'offered' AND expires_at > now() RETURNING request_id, provider_id, quote_id;. The affected-row count is the authoritative answer to 'did I win', and nothing else in the system is. - Explain why that is safe at READ COMMITTED specifically. When the sweeper holds the row lock, the accept's UPDATE blocks; on the sweeper's commit PostgreSQL re-evaluates the WHERE clause against the new row version, finds
status = 'expired', and matches zero rows. A priorSELECTwould have read a snapshot taken before the sweeper committed and decided on stale data, with nothing re-checking it. - Map both outcomes to caller behaviour. One row means you won: insert the booking and the outbox row in the same transaction, and keep the payment gateway call outside it. Zero rows means you lost, and the caller must re-read to tell the provider why — expired, rescinded, or already declined — and must not retry the UPDATE, because a retry cannot change a terminal status.
- Give the sweeper the mirror-image guard so the two cannot both win:
UPDATE dispatch_offer SET status = 'expired' WHERE status = 'offered' AND expires_at <= now();. - Push the cross-partition duplicate down to the storage layer, since partition-local reasoning cannot see it:
CREATE UNIQUE INDEX CONCURRENTLY provider_one_live_offer ON dispatch_offer (provider_id) WHERE status = 'offered';. The losing partition's INSERT raises SQLSTATE 23505, which the dispatcher treats as 'candidate taken, take the next one' rather than as an error. Pair it with the same shape onbooking (provider_id) WHERE status IN ('accepted','en_route','arrived','in_progress'). - Compare with the isolation-level answer: SERIALIZABLE prevents the same anomaly but reports it as a 40001 serialization failure that every caller must be written to retry, and the retry arrives at the same decision — so you buy retry machinery to learn what the affected-row count already told you for free.
Follow-up
- The offer transition, the booking insert and the outbox row must commit together. Sketch that transaction, and say what goes wrong if the payment authorisation is inside it.
- A partial unique index's predicate must be immutable. Why can it not be
WHERE expires_at > now(), and what do you use instead? - The provider's phone times out and retries the accept, but the first call actually succeeded. What does the second call return under your design, and is that acceptable?
Add a no-overlap reservation constraint to a live table
booking holds 400 million rows and takes roughly 1,200 writes per second. Rental and freelance bookings store start_at and end_at, and overlaps are currently prevented by an application-level select-then-insert that has produced 61 double bookings in six months. Add reserved_during TSTZRANGE and enforce non-overlap per listing_id across non-cancelled rows. Give the ordered migration, the exact statements, the lock each one takes, and say what you do about the step that has no concurrent path.
Approach
ALTER TABLE booking ADD COLUMN reserved_during tstzrange;— nullable with no default is catalog-only and does not rewrite the table, but it still takes ACCESS EXCLUSIVE briefly, and that request queues behind any open transaction while blocking every reader that arrives behind it. So each DDL statement runs withSET lock_timeout = '2s'and a retry loop. Reject the tidierGENERATED ALWAYS AS (tstzrange(start_at, end_at, '[)')) STOREDform: adding a stored generated column rewrites all 400 million rows under that lock.- Deploy dual-write before backfilling, not after. The application writes
reserved_duringalongsidestart_atandend_aton every insert and update; backfilling first leaves a growing gap of rows the backfill has already swept past. - Backfill by keyset pagination over the primary key in batches of a few thousand, one transaction per batch, pausing between batches:
UPDATE booking SET reserved_during = tstzrange(start_at, end_at, '[)') WHERE booking_id > $last AND booking_id <= $last + $n AND reserved_during IS NULL AND start_at IS NOT NULL;. Every touched row is rewritten, so this is 400 million dead tuples' worth of vacuum pressure and WAL; pace the batches against replication lag andn_dead_tuprather than running as fast as the database accepts. - Find the pre-existing violations before you attempt the constraint, because the 61 known double bookings will abort the DDL only after it has already built the entire index. Build
CREATE INDEX CONCURRENTLY booking_listing_range_idx ON booking USING gist (listing_id, reserved_during) WHERE status <> 'cancelled';(needsCREATE EXTENSION btree_gist), then self-join witha.listing_id = b.listing_id AND a.booking_id < b.booking_id AND a.reserved_during && b.reserved_duringand resolve each conflict as a product decision, not a data edit. - Confront the step with no concurrent path:
ALTER TABLE booking ADD CONSTRAINT booking_no_overlap EXCLUDE USING gist (listing_id WITH =, reserved_during WITH &&) WHERE (status <> 'cancelled');takes ACCESS EXCLUSIVE and builds its own index inline. There is noCREATE INDEX CONCURRENTLYequivalent,ADD CONSTRAINT ... USING INDEXaccepts only UNIQUE and PRIMARY KEY indexes, andNOT VALIDapplies to CHECK and foreign-key constraints, not to exclusion constraints. Either take a bounded window sized by a timed build on a sample, or get the invariant from aBEFORE INSERT OR UPDATEtrigger that first runsSELECT 1 FROM listing WHERE listing_id = NEW.listing_id FOR UPDATE, serialising writers per listing so the subsequent overlap check is safe. Say which you chose, and say that the trigger is the weaker of the two because it can be disabled and the constraint cannot. - Retire the old columns in a separate deploy: stop reading
start_atandend_atfirst, then drop them. Dropping them while any read path still uses them converts a reversible migration into an outage.
Worked solution 45 min
- On a copy sized to a few million rows, time the constraint build and extrapolate; that figure is the outage budget you are about to request.
- Run the ADD COLUMN with
lock_timeoutset while a longSELECTis open, and confirm it fails fast instead of queueing. - Backfill one batch and measure dead-tuple growth and WAL volume per thousand rows, then multiply out to 400 million.
- Run the overlap self-join and confirm the known 61 conflicts surface before any constraint is attempted.
- Attempt the constraint against data that still contains one overlap and observe where in the statement it fails.
Follow-up
bookingis partitioned bymarket_id, and PostgreSQL does not allow an exclusion constraint on a partitioned table. What does a per-partition constraint actually guarantee here, and is it enough?- The backfill reaches row 180 million and replica lag hits 40 seconds. What do you change, and what do you deliberately leave alone?
- Half-open
[)or closed[]for the range: which did you choose, and what happens to a check-out at 11:00 followed by a check-in at 11:00 under each?
Design the high-level architecture of a notification system that alert…
Design the high-level architecture of a notification system that alerts hosts of new booking requests in real time.
Approach
- Design the error taxonomy before the success shape; callers branch on it.
- State how the contract changes without breaking existing clients.
- Define the identity of a request so a retry cannot double-apply it.
Follow-up
- What happens if the caller retries after a timeout?
- How does a client discover it is on an old version of this contract?
Design a robust API for a specific feature of the Turo platform, detai…
Design a robust API for a specific feature of the Turo platform, detailing the endpoints, HTTP methods, request bodies, response payloads, and status codes.
Approach
- Say who the caller is and what they do when the call fails halfway.
- Separate accepted, pending, failed and confirmed; they are different facts.
- Design the error taxonomy before the success shape; callers branch on it.
Follow-up
- What happens if the caller retries after a timeout?
- What does a partial failure look like to the caller?
Paginate an availability search whose result set mutates mid-scroll
A consumer app lists available listings in a market, ranked by a score mixing distance and the current demand multiplier, twenty per screen. Between page one and page three some listings are booked, the multiplier for a cell refreshes about once a minute, and new supply comes online. With LIMIT/OFFSET, users report seeing one listing twice and never seeing others. Design the pagination contract: the ordering key, what the cursor contains and whether clients may construct one, page-size limits, and the response when a cursor is older than the ranking it was cut from.
Approach
- Name the actual defect before choosing a mechanism. OFFSET is a position in an ordering, so when a row ahead of the cursor disappears every later row shifts up by one and page two silently skips an item; when the multiplier refreshes and a row moves up past the boundary, it appears on two pages. OFFSET also costs the server the rows it discards, so the last page is the most expensive one.
- Make the ranking stable for the session instead of stabilising the cursor. Persist a search context - search_id, the filter set, the multiplier snapshot per cell, created_at - with a short TTL of a few minutes, and score every page from that snapshot. Keyset pagination is only correct when the sort key does not move underneath it, and here the key is derived from an input that moves every minute.
- Use a keyset predicate over the frozen score plus a unique tie-break, and keep both keys in one direction so the row-value form is legal: WHERE (score, listing_id) < (:score, :listing_id) ORDER BY score DESC, listing_id DESC LIMIT 20, served by a composite index on (score DESC, listing_id DESC) - the same btree as (score, listing_id) read backwards. A row-value comparison is an index qual there, not a filter, so a deep page costs one descent like page one does. A mixed ordering has no row-value form at all: for score DESC, listing_id ASC you must expand to score < :s OR (score = :s AND listing_id > :id), each comparator matching its own column's direction, over an index declared (score DESC, listing_id ASC); expect the planner to range-scan on score and apply the tie-break as a filter, which is fine only while tie groups stay small. Copying the uniform-direction expansion - listing_id < :id - under a mixed ORDER BY re-serves part of the tie group you just returned and skips the rest, which is the duplicate-and-skip symptom you started with, and it stays invisible until two listings tie on score.
- Let pages be short rather than backfilling. A listing booked since the snapshot is dropped or returned with an availability flag the client greys out; silently topping the page back up to twenty is what reintroduces duplicates. Omit a total count entirely - it is stale before it renders and forces a count over the whole candidate set.
- Make the cursor opaque and tamper-evident: base64url of a versioned, HMAC-signed payload holding search_id, the last (score, listing_id) and the page size. Signing stops a hand-built cursor from pinning the server to a predicate no index serves; versioning lets the encoding change later. An expired search context returns a distinct machine code that tells the client to restart from page one, rather than re-ranking under it.
Worked solution 25 min
- Write out the score expression and mark which inputs move during a browsing session: the multiplier refresh, newly booked listings, newly onboarded supply.
- Define the search context record and its TTL, and make every page recompute the score from the snapshot rather than from live inputs.
- Write the keyset predicate and the exact index that serves it, then check the plan for a page deep in the result set.
- Simulate the reported bug: fetch page one, book out three of the top twenty, refresh the multiplier, then fetch page two and assert the union of ids has no duplicate and no gap against the snapshot.
- Request page four after the context TTL has passed and confirm the response carries the restart code rather than a silently re-ranked page.
Follow-up
- The client wants to jump to page 10 directly. What do you offer, given that a keyset cursor only walks forward?
- How would the contract change if a listing's availability had to be exact at render time rather than as of the snapshot?
- Two devices share one account and scroll simultaneously. Does the search context belong to the account, the device, or the request?
Quote latency spikes every sixty seconds during a demand-signal outage
The demand multiplier is cached per (cell, tier) with a 60-second TTL and refreshed from an aggregator. The aggregator starts returning 504s for about 30% of cells. Quote p99 goes from 40 ms to 3.1 s in a sawtooth with a 60-second period, aggregator load triples although request volume is flat, and the dispatch loop begins missing its two-second budget so accept windows are wasted. Each refresh retries three times with a two-second timeout. Give the ordered checklist, the two independent defects, and the degraded behaviour you ship.
Approach
- Chart cache hit rate per cell at one-second resolution first. A sawtooth whose period equals the TTL is the signature of synchronized expiry -- entries written together by a warm-up or by an earlier mass refresh expire together -- and it distinguishes a stampede from a dependency that is merely slow, before you touch the dependency at all.
- Then put origin call rate next to incoming request rate. Load tripling while requests stay flat is retry amplification, not demand: three attempts per miss, and each concurrent miss on the same key mounts its own set of attempts because nothing coalesces them. The multiplier falls straight out of the ratio.
- Then characterise the dependency's failure shape -- which cells, which regions, whether the 504s are uniform or concentrated. A partial failure across 30% of cells is a different incident from a whole-region loss, and the correct degraded behaviour differs between them.
- Separate the two defects, because fixing either alone leaves the incident standing. First, there is no single-flight, so N concurrent misses on one key produce N origin calls rather than one. Second, refresh treats an error as absence and evicts, so a failing dependency empties the cache instead of leaving it populated. Independently each is survivable; together they convert a 30% dependency failure into a cache with a near-zero hit rate.
- Ship the degraded behaviour explicitly rather than leaving it implicit. Serve the last known multiplier past its TTL under a bounded staleness ceiling, and once that ceiling is exceeded fall back to the base rate rather than to the last surge value -- charging a multiplier you can no longer justify is the asymmetric error and it surfaces as disputes and chargebacks rather than as an alert. Add per-key single-flight, jitter the TTL so entries stop expiring in lockstep, and replace per-call retry counts with a retry budget capped as a fraction of total calls behind a circuit breaker.
- State what does not change, because this is the part people break while fixing the rest. Quotes already issued are immutable and stay honoured for their full lifetime; the booking binds to quote_id, so the degraded path must never reprice a booking already bound to one. A quote that expires mid-flow is a user-visible re-quote, not a silent background refresh.
Follow-up
- Single-flight makes one caller wait on the origin. What does that waiter do when the origin exceeds the dispatch budget, and who decides -- the cache or the caller?
- You choose a five-minute staleness ceiling. Justify the number against what the multiplier actually does over five minutes in a dense cell.
- The aggregator recovers. Describe the recovery traffic and what stops it from becoming a second stampede.
For someone fluent in a dynamic language who has shipped real work but has never had to say what the runtime is doing underneath. The week is built on measuring and deliberately breaking things, because the questions that expose this background are the ones where the interviewer asks why a second time.
Prepare, practise & reflect
One practical outcome each day. Spend longer where you need it.
0 / 7 done01Recruiter screen: project stories and a specific 'why Turo'
- Write two-sentence summaries of your three strongest projects with no internal system names: what was breaking, what you changed, what happened after. Say them to someone outside engineering and fix whatever they repeat back wrong.
- Use the Turo app as a guest and read how hosting works, then write down the search and booking flows you saw, from both the guest and host side. Base your answer to 'why Turo, and how do you relate to its peer-to-peer marketplace model?' on one of those flows.
- List your questions for the recruiter: which team, whether the work leans backend, web or mobile, whether a take-home is involved, and whether the technical screen is coding or API design.
Deliverable: Three project summaries, a 'why Turo' answer anchored on a specific product flow, and a written list of recruiter questions.
Practice prompt ↗Practice prompt ↗Worked solution ↗02Coding: arrays, strings, intervals and grids
- Solve two array or string problems. Write the brute force first, then improve it, and state time and space for both versions.
- Solve merge-overlapping-intervals, then an availability-slot problem: given a vehicle's bookings and a search window, return the free slots. Test empty input, touching bookings, and one booking nested inside another.
- Work the 'Find overlapping reservations and the largest free gap' worked exercise and check your sweep against its expected result.
- Solve a grid word-search problem with DFS and backtracking, and state its complexity in terms of grid size and word length.
Deliverable: Five solved problems plus the worked exercise, each problem with stated complexity and the edge cases you tested written underneath.
Practice prompt ↗Practice prompt ↗03Coding: an in-memory cache with eviction and tests
- Implement an LRU cache with O(1) get and put, using a hash map with a doubly linked list or an insertion-ordered map (for example LinkedHashMap or OrderedDict). A key-sorted map such as TreeMap does not track recency. State the eviction policy before writing code.
- Write the boundary tests: fill to exactly capacity (nothing evicted), add one more (least recently used key gone), read an old key before that insert (victim changes), capacity zero, and updating an existing key.
- Add a property test that compares your cache against a slow reference (a list scanned linearly) over random operation sequences.
- Extend the cache with per-entry expiry and explain what changes if two threads call it at once.
Deliverable: A tested LRU cache with boundary tests, one property test and a short note on expiry and thread safety.
Practice prompt ↗Practice prompt ↗04SQL and data modeling: booking metrics and schemas
- Build a small dataset of hosts, vehicles and bookings that includes a vehicle with no bookings, a cancelled booking and a booking that straddles the range edge. Write the booking-metrics query for a host's vehicles over a time range using a LEFT JOIN with filters in ON, COUNT on the booking key and COALESCE on sums.
- Design tables for vehicles, bookings and reviews (guests rate cars, hosts respond) with primary and foreign keys, and justify each index by the query it serves.
- Work the 'Add a no-overlap reservation constraint to a live table' worked exercise, then attempt the accept-versus-expire race drill and write the guarded UPDATE yourself.
Deliverable: A booking-metrics query verified against your dataset, a review-system schema with justified indexes, and your answers to both SQL drills.
Practice prompt ↗Practice prompt ↗Worked solution ↗05API design: booking contracts
- Design a trip booking REST API and schema: resources, endpoints, HTTP methods, request bodies, response payloads and status codes for search, create, modify and cancel.
- Write the error taxonomy first (validation, date conflict, not found, payment failure) and map each error to a status code and a machine-readable error body.
- Add an idempotency key to booking creation and describe what a client sees if it retries after a timeout.
- Work the 'Paginate an availability search whose result set mutates mid-scroll' worked exercise and add its cursor contract to your search endpoint.
Deliverable: A written API contract for booking with endpoints, payloads, status codes, error bodies and a pagination scheme.
Practice prompt ↗Practice prompt ↗06System design: request flow, notifications and read scaling
- Trace a car-booking request from browser to database out loud: DNS, load balancer, application tier, cache, primary and replicas. Mark where stale data is acceptable and where it could cause a double booking.
- Design a real-time notification system that alerts hosts of new booking requests, covering the queue, retries, deduplication and what happens when delivery fails.
- Design how you would scale a read-heavy service with distributed caching, message queues and database replication, then work the quote-latency debugging drill on cache stampedes and retry amplification.
- Outline how HTTP requests are routed to pods in a cluster and how you would plan a major database version upgrade, both topics from the reported question list.
Deliverable: Three one-page design sketches (request flow, host notifications, read scaling), each listing the failure every component prevents.
Practice prompt ↗07Mock onsite and behavioral stories
- Write stories for the reported behavioral prompts: a tight deadline with ambiguous requirements, a disagreement with a product manager over feasibility versus scope, and a complex production issue you debugged. Name the person, the decision and the result in each.
- Add a story about a complex project decision and how you made the result observable, and one about a security improvement you drove.
- Run three mocks back to back (coding, API or system design, behavioral) and ask each interviewer where you answered in the wrong mode.
- Review your notes from days 2 to 6 and redo the one problem you found hardest without looking at your earlier answer.
Deliverable: Five written behavioral stories, feedback from three consecutive mocks, and one re-solved problem.
Practice prompt ↗Worked solution ↗Expand any day for tasks and deliverables. Your progress is saved on this device.
Reported behavioral questions for this role focus on working with ambiguity, collaborating with product managers, debugging production issues and motivation for Turo. For each one, pick a story where you made the decision. Name the people involved and the disagreement, then say what you did, what happened and what you would change. For ambiguous requirements, show how you fenced the problem off: the assumption you wrote down, who confirmed it, and the narrow version you shipped first.
Tell me about a time you had to deliver a project under tight deadline…
Tell me about a time you had to deliver a project under tight deadlines with ambiguous requirements. How did you prioritize?
Approach
- State the situation in two sentences and spend the rest on the reasoning.
- Give the blast radius: what could have broken, and what you measured.
- Name the disagreement and how you resolved it with evidence.
Follow-up
- What did you decide not to do, and why?
- How did you know your change caused the improvement?
Describe a situation where you had to debug a complex production issue…
Describe a situation where you had to debug a complex production issue. What was your approach, and what did you learn?
Approach
- Give the blast radius: what could have broken, and what you measured.
- Pick a story where you made the decision, not one where you watched it.
- Close with what you would do differently, concretely.
Follow-up
- What did you decide not to do, and why?
- How did you know your change caused the improvement?
Unblocking a stuck engineer without taking the keyboard
Describe a time you unblocked someone who had been stuck for more than a day. Say how you learned they were stuck, what you diagnosed the real blocker to be - missing context, a wrong mental model, a genuinely hard bug, or reluctance to ask - and what you actually did. State explicitly whether you took the keyboard and what that cost. Then say what changed so the next person is not stuck in the same place: a document, a test, a renamed function, a constraint that turns the mistake into an error.
Approach
- Diagnose the blocker type first, because the responses diverge sharply. Missing context is a five-minute fix; a wrong mental model has to be corrected out loud and checked; reluctance to ask is a team-norm problem you cannot solve in one sitting and should not pretend you did.
- Describe the intervention at the level of what you said or drew - the interleaving you sketched, the question that surfaced their hidden assumption - rather than the word 'pairing', which conveys nothing about what you contributed.
- Be explicit about taking the keyboard. It is sometimes right under time pressure and it always trades their learning for your speed. Naming the trade is the difference between mentoring and rescuing.
- Verify the unblock rather than assuming it: did they finish it alone, did they hit the same wall a fortnight later, did they later explain it to someone else. That last one is the strongest available evidence.
- Name the durable artifact. A mentoring answer with no residue describes one act of help; the leverage is in the test, the comment, or the constraint that makes the same confusion impossible next time.
Follow-up
- How long did you let them struggle before stepping in, and how did you choose that duration?
- What did you get wrong about why they were stuck?
- When is taking the keyboard the correct call rather than the easy one?
- 01
Tell me about a time you had to deliver a project under tight deadlines with ambiguous requirements. How did you prioritize?
- 02
How do you collaborate with a Product Manager when there is a disagreement on technical feasibility versus product scope?
- 03
Describe a situation where you had to debug a complex production issue. What was your approach, and what did you learn?
- 04
Why do you want to work at Turo, and how do you personally relate to its peer-to-peer marketplace model?
- 05
Walk me through a complex project decision you made and how you made the result observable in production.
- 06
Tell me about a time you unblocked someone who had been stuck for more than a day. What was the real blocker, and what changed afterwards?
Is this an official Turo interview guide?
No. It is PracHub's own research and practice material for the Software Engineer role at Turo. Rounds and questions reflect what candidates have reported, not a process Turo has published, and they change over time. Confirm the current format and scope with your recruiter.
PracHub interview research ↗What rounds do Turo Software Engineer candidates report?
Candidates report three stages over roughly three to five weeks: a recruiter screen, a technical screen focused on coding or API design, and a multi-round virtual onsite that assesses technical and cultural fit. The reports do not say which onsite interview covers which topic, so prepare coding, SQL, API design, system design and behavioral questions for it.
PracHub Software Engineer practice ↗What is the technical difficulty of the coding interviews?
Candidates describe the coding interviews as average in difficulty. The reports emphasize practical problem-solving, clean code and database work over advanced dynamic programming. Reported examples include an in-memory cache with eviction, finding available time slots in availability data, and array and string problems. Readable, tested code and handled edge cases matter more than an exotic algorithm.
PracHub interview research ↗Does Turo use take-home assignments?
Some candidates report a take-home before the onsite, depending on team and role. Examples mentioned are building a simple application that consumes a public API or writing a small mobile app. Ask your recruiter early whether your process includes one. If it does, treat structure, tests and a short note on what you left out as part of the submission.
PracHub interview research ↗How can I stand out in the API design round?
Go beyond listing endpoints. Define request and response schemas, explain your choice of HTTP methods and status codes, and design the error cases: invalid input, a date conflict, a missing resource. Say how a client retries creating a booking without creating two, and tie your choices back to what the guest or host sees in the app.
PracHub interview research ↗How much SQL should I prepare?
Enough to write joins and aggregations fluently and handle nulls deliberately. A reported question asks for booking metrics on specific host vehicles over a time range, handling null values and joins across multiple tables. Practise LEFT JOINs that keep vehicles with no bookings, filters placed in ON rather than WHERE, COALESCE on aggregates, and schema design with keys and indexes.
PracHub Software Engineer practice ↗Do reported questions include mobile or frontend topics?
Yes. The reported question list includes a Retrofit observable return type question, a TableView in Storyboard question and a JavaScript custom array prototype with async question, and the role notes mention React and Next.js for web work. Ask your recruiter whether your team is backend, web or mobile, and weight your preparation toward that stack on top of the shared coding, SQL and API topics.
PracHub Software Engineer practice ↗What should I do if I stop hearing back between stages?
Some candidates report administrative delays or scheduling problems. At the end of each stage, confirm the next step and when you should expect to hear. If that date passes, send a short, polite follow-up to your recruiter.
PracHub Software Engineer practice ↗Sources & methodology 3 sources ↗
No official company page is cited. Rounds and questions come from candidate reports and PracHub editorial material; each source shows the date it was read.
- 01PracHub interview research ↗
PracHub editorial research into this company and role, maintained with this guide. Candidate-reported, not an employer publication.
PracHub page · Accessed 2026-09-24 - 02PracHub Software Engineer practice ↗
Cross-company practice questions for this role.
PracHub page · Accessed 2026-09-24 - 03PracHub interview preparation framework ↗
The framework the preparation plan follows.
PracHub page · Accessed 2026-09-24