AO
Back

Bootstrap API: Aggregate User, Payment and Address Services, Tolerating Failures

Also reported as Code Craft Bootstrap API · Bootstrap / Aggregator API · BootstrapService

Phone ScreenPhone ScreenLast reported September 2026Medium Frequency

Problem Overview

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]:
  • Call the consumer service first. If it fails (a non-200 status, a timeout, or no consumer_id in the body), return (500, {"error": "consumer service unavailable", "user_id": user_id}) and do not call the others.
  • Otherwise return 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
  • A failure is a non-200 status or a 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]:
  • Same response rules. A call that answers 5xx or times out is tried again, up to 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.

Follow-up Arc

Interviewers escalate through these phases. The order varies, but most candidates see at least one from each bucket.
Trade-off discussion · 9
Trade-off discussion

In what order do you call the three upstream APIs?

Probes for: explaining the design

How do you handle timeouts in a real system? Retry after a timeout, or discard?

Probes for: after the base version

Besides catching exceptions, are you checking the response status code, such as 500?

Probes for: reviewing the error handling

Does every upstream failure have to return 500? If some information is still available, should you show it?

Probes for: discussing failures

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?

Probes for: partial failures

Run the code. Why did it return 500? Is the same problem anywhere else?

Probes for: running the code

Does the address include the first and last name from the expected response?

Probes for: checking the output

Test the consumer service failing, and each of the other upstreams failing. What status and fields come back?

Probes for: testing

When should the API fail open and when fail closed? What retry patterns would you use?

Probes for: fault-tolerance discussion

Approach Trade-offs

Approaches actually attempted in reports — including ones that lost candidates time. Pick deliberately.
ApproachNotes
Call payment and address concurrently once the consumer_id is knownlower latency; not required in the reports that mention it
Default values instead of None for a failed sectionask what the client expects
A circuit breaker around each upstreamproduction discussion, not something to build in the hour

What Reports Emphasize

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

Practice

Write your own against 11 test cases, or read the worked solution — approach, complexity, and code that runs.

Prepping for DoorDash with friends?

Send them this page. It is free to read, no account needed.

More DoorDash Questions

Free preview

Every question in the DoorDash catalog gets this depth

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.

$59/mo — or $50/mo with the 3-month pass · cancel anytime
DoorDash · Phone Screen · Reported 9× across candidate reports
Is this helpful?