SWE / Backend · Code Craft / Phone

DoorDash Bootstrap API:三服务聚合、依赖顺序与部分失败

DoorDash Code Craft 可追溯题:用 userId 查 customerId,再并发取支付卡和地址,明确 500、缺字段、超时与部分失败策略。

题目重点:DoorDash Code Craft 可追溯题:用 userId 查 customerId,再并发取支付卡和地址,明确 500、缺字段、超时与部分失败策略。

题意与假设

这道练习题保留了面经中值得训练的核心约束,并将需要澄清的条件单独列出。现场拿到变体时,先和面试官确认这些条件。

  • 可见题面规定 UserService(userId) 先返回 customerId,随后 PaymentService(customerId) 和 AddressService(customerId) 独立调用。示例代码接收三个已给定的 response map,模拟可执行编辑器,不假装它负责真正发起网络请求。
  • 示例采用明确的训练策略:用户身份请求失败则报错;卡或地址失败时返回 null,并在 Errors 列出原因。题目把 fallback/default 作为面试讨论点,没有指定唯一正确策略。
  • 三个 response map 形如 {statusCode, data}。示例假设成功码为 200、customerId 为非空字符串、card 为对象、address 为非空字符串;具体字段名以现场代码为准。
  • 的 Java starter 是更宽的同题版本:用户响应含 roles,支付响应含 giftCards,期望输出还有 availableGiftCardCredits 与 formattedAddress;本 Python map 示例按 的较窄公开契约,不把那些字段强加给所有版本。

解题路线

  1. 先画依赖图:UserService 是串行屏障;拿到 customerId 后,支付和地址两条调用可并发。真实服务封装要传同一 customerId,并用一次请求 deadline 限制尾延迟。
  2. 把 HTTP 失败与成功响应缺字段分开处理。身份缺失不能安全拼接其他用户数据;可选字段失败按产品确认的部分成功契约返回,并让调用方能识别缺失原因。
  3. 仅对幂等读请求的可重试失败做有上限的退避重试,限定总超时与并发数。不要把每个 500 无限制重试;缓存兜底要检查用户绑定、数据新鲜度和隐私要求。

手动走一遍

UserService 对 userId=u7 返回 customerId=c1。PaymentService 返回卡尾号 4242,AddressService 返回 Main St,则聚合为 CustomerId=c1、DefaultCard={card_last_four:4242}、Address=Main St。若地址 500,示例返回 Address=null、Errors=[address_unavailable];若 UserService 500,直接报错,不拼出假身份。

Python 参考实现

下面的代码按上文假设实现,适合本地运行并追加自己的测试用例。

代码区可横向滚动;使用键盘时,先聚焦代码区再按方向键查看。

"""Training contract for the visible DoorDash Bootstrap API prompt.

The editor's three supplied maps stand in for upstream service responses.
"""

from collections.abc import Mapping


class BootstrapError(ValueError):
    pass


def _payload(response: Mapping, service: str) -> Mapping | None:
    if response.get("statusCode") != 200:
        return None
    data = response.get("data")
    if not isinstance(data, Mapping):
        raise BootstrapError(f"{service}: missing data map")
    return data


def bootstrap_api(
    user_response: Mapping,
    payment_response: Mapping,
    address_response: Mapping,
) -> dict:
    """Fail closed on identity; return explicit nulls for optional failures.

    The caller must ensure payment/address maps were fetched for the returned
    customerId. This function cannot prove upstream request provenance.
    """
    user = _payload(user_response, "user")
    if user is None:
        raise BootstrapError("user service unavailable")
    customer_id = user.get("customerId")
    if not isinstance(customer_id, str) or not customer_id:
        raise BootstrapError("user response has no customerId")

    errors: list[str] = []
    payment = _payload(payment_response, "payment")
    if payment is None:
        errors.append("payment_unavailable")
    elif not isinstance(payment.get("defaultCard"), Mapping):
        errors.append("payment_card_missing")
    card = dict(payment["defaultCard"]) if payment and isinstance(payment.get("defaultCard"), Mapping) else None

    address_data = _payload(address_response, "address")
    if address_data is None:
        errors.append("address_unavailable")
    address = address_data.get("address") if address_data else None
    if address_data is not None and (not isinstance(address, str) or not address):
        errors.append("address_missing")
        address = None

    return {
        "CustomerId": customer_id,
        "DefaultCard": card,
        "Address": address,
        "Errors": errors,
    }

复杂度

设响应数据总大小为 S,映射与卡字段复制 O(S) 时间、O(S) 输出空间。真实服务的关键是延迟:Tuser + max(Tpayment,Taddress) 加有界重试时间;若顺序调用则为三者之和。

易错点与追问

容易失分的边界

  • 把三个服务同时发出会在 customerId 尚未确定时请求支付/地址,违反可见依赖;但拿到 customerId 后继续串行又无谓增加延迟。
  • 默认空卡或空地址会被前端误当成真实值。部分失败需要显式错误标志;如果产品要求原子全量响应,则应改为整体失败,不能悄悄混用契约。
  • 本示例的 map 接口无法验证 payment/address 响应属于哪个 customerId,真实客户端必须在请求层保证参数绑定,缓存 key 也应含 customerId。

继续深挖

  • 若下游服务可能超时,在整体 deadline 内分配各调用预算,并区分超时、5xx 与永久性 4xx;重试加 jitter 且尊重预算。
  • 若需要服务结果逐步显示,可定义每块独立 loading/error 状态;若必须一次返回全量,明确哪些字段是关键字段,再写相应失败测试。
  • 对生产测试覆盖 8 种上游成功/失败组合、缺 customerId、缺 card/address、不同 customerId 的缓存隔离和重复请求。
  • 若采用 Java starter 变体,另确认 gift card credits 的数值/货币格式、roles 来源与 formattedAddress 中姓名拼接规则;不要直接从示例输出反推完整规范。
✓已复制微信号!