def bootstrap(user_id, consumer_service, payment_service, address_service):
The round: a DoorDash Code Craft round, reported nine times: usually the phone screen (60 minutes in one report), and in one report an onsite round. You implement a Bootstrap API that takes a user_id, calls three existing upstream services, and combines their answers into one response, staying as useful as possible when an upstream fails. Reports say boilerplate and mock services are given and you write your own tests. One interviewer had the candidate run the code and asked why it returned 500, and one checked a long nested expected response field by field. Retries are an optional extension after the base version in the reports; this page grades them as Part 2. Concurrency was not required in the reports that mention it. Two graded parts here.
Given — what the tests call
There is no network on this page. Each upstream is a MockService: service.get(key) returns a Response with status_code and body (a dict on 200, None otherwise), or raises TimeoutError.
consumer_service.get(user_id) # {"consumer_id": "c-77", "roles": ["consumer"]}
payment_service.get(consumer_id) # {"default_card": {...}, "gift_card_credit": 1500}
address_service.get(consumer_id) # {"formatted_address": "...", "first_name": "...", "last_name": "..."}
user_id and consumer_id are different ids: payment and address are looked up by the consumer_id the consumer service returns.
Part 1 — aggregate three services, tolerating failures
def bootstrap(user_id, consumer_service, payment_service, address_service) -> tuple[int, dict]:
consumer_id in the body), return (500, {"error": "consumer service unavailable", "user_id": user_id}) and do not call the others.200 and this body:{"user_id": ..., "consumer_id": ..., "roles": [...],
"payment": {"default_card": ..., "gift_card_credit": ...}, # or None if payment failed
"address": {"formatted_address": ..., "first_name": ..., "last_name": ...}, # or None
"errors": ["payment", "address"]} # the sections that failed, in that order; [] if none
TimeoutError. A 500 does not raise, so check the status code, not just exceptions.Notes: the field names, the response shape, the errors list, treating a 404 from the consumer service as a 500, and max_attempts=3 are ours. The reports give the three services, their inputs and the data each provides, the 500-on-consumer-failure rule and partial responses for payment or address failures. No delay between retries: on this page nothing can sleep usefully, and the reports do not say.
Interviews reported from February to September 2026, usually as the Code Craft phone screen. Reports describe given boilerplate with mock services and an interviewer who drives with questions: in what order do you call the services, what happens when each fails, do you check status codes, why did your run return 500. One interviewer was described as pressing hard on small mistakes in a long nested expected object, string formatting included. One candidate who passed said the function plus error handling was enough, with the interviewer adding error-handling asks after the base version. Another spent the round on Java concurrency and only ran the happy path. Fail-open versus fail-closed and retry patterns came up in one screen.
Also reported as Code Craft Bootstrap API · Bootstrap / Aggregator API · BootstrapService
The round: a DoorDash Code Craft round, reported nine times: usually the phone screen (60 minutes in one report), and in one report an onsite round. You implement a Bootstrap API that takes a user_id, calls three existing upstream services, and combines their answers into one response, staying as useful as possible when an upstream fails. Reports say boilerplate and mock services are given and you write your own tests. One interviewer had the candidate run the code and asked why it returned 500, and one checked a long nested expected response field by field. Retries are an optional extension after the base version in the reports; this page grades them as Part 2. Concurrency was not required in the reports that mention it. Two graded parts here.
Given — what the tests call
There is no network on this page. Each upstream is a MockService: service.get(key) returns a Response with status_code and body (a dict on 200, None otherwise), or raises TimeoutError.
consumer_service.get(user_id) # {"consumer_id": "c-77", "roles": ["consumer"]}
payment_service.get(consumer_id) # {"default_card": {...}, "gift_card_credit": 1500}
address_service.get(consumer_id) # {"formatted_address": "...", "first_name": "...", "last_name": "..."}
user_id and consumer_id are different ids: payment and address are looked up by the consumer_id the consumer service returns.
Part 1 — aggregate three services, tolerating failures
def bootstrap(user_id, consumer_service, payment_service, address_service) -> tuple[int, dict]:
consumer_id in the body), return (500, {"error": "consumer service unavailable", "user_id": user_id}) and do not call the others.200 and this body:{"user_id": ..., "consumer_id": ..., "roles": [...],
"payment": {"default_card": ..., "gift_card_credit": ...}, # or None if payment failed
"address": {"formatted_address": ..., "first_name": ..., "last_name": ...}, # or None
"errors": ["payment", "address"]} # the sections that failed, in that order; [] if none
TimeoutError. A 500 does not raise, so check the status code, not just exceptions.Part 2 — retry transient failures
def bootstrap(user_id, consumer_service, payment_service, address_service, max_attempts: int = 3) -> tuple[int, dict]:
max_attempts calls in total for that service. A 4xx (such as 404) will not change, so it is not retried. Test one failure that recovers and repeated failures that do not.Notes: the field names, the response shape, the errors list, treating a 404 from the consumer service as a 500, and max_attempts=3 are ours. The reports give the three services, their inputs and the data each provides, the 500-on-consumer-failure rule and partial responses for payment or address failures. No delay between retries: on this page nothing can sleep usefully, and the reports do not say.
In what order do you call the three upstream APIs?
How do you handle timeouts in a real system? Retry after a timeout, or discard?
Besides catching exceptions, are you checking the response status code, such as 500?
Does every upstream failure have to return 500? If some information is still available, should you show it?
What do you return if the user and payment calls succeed but address fails? What if payment and address both fail but you still have the user?
Run the code. Why did it return 500? Is the same problem anywhere else?
Does the address include the first and last name from the expected response?
Test the consumer service failing, and each of the other upstreams failing. What status and fields come back?
When should the API fail open and when fail closed? What retry patterns would you use?
| Approach | Notes |
|---|---|
| Call payment and address concurrently once the consumer_id is known | lower latency; not required in the reports that mention it |
| Default values instead of None for a failed section | ask what the client expects |
| A circuit breaker around each upstream | production discussion, not something to build in the hour |
Common mistakes: Not checking the HTTP status code, so a 500 response is treated as success; Returning 500 when only payment or address failed; Looking up payment or address by user_id instead of consumer_id; Missing or misformatted fields in a long nested expected response; Spending the time on concurrency and only running the happy path.
What passers do: One candidate who passed got the function working and then added error handling as the interviewer asked, and said that was enough.
Edge cases to test: The consumer service fails (500 or timeout); Only payment fails; Only address fails; Payment and address both fail; A 500 that does not raise an exception; One failure that recovers on retry; Repeated failures that do not
Send them this page. It is free to read, no account needed.
What you just read — canonical solution, follow-up arc, what passing candidates actually did — exists for all 12 DoorDash questions, refreshed monthly from new candidate reports.