DoorDash’s software engineering internship program is built to be both hands-on and highly impactful. Our interns join Engineering teams directly, where they work on real features and tools used by Dashers, merchants, consumers, and employees across the company. This post is part of a 4-part series highlighting the technical accomplishments of our Summer 2025 interns - check out Part 1, Part 2, and Part 3.
From Bulk Tools to Logistics Console: Improving ETA Reliability During High-Stress Events
By Zijian Wei
DoorDash’s generalized model for determining estimated times of arrival (ETAs) can be thrown off during times of duress in a particular area, such as when an event causes unexpected delays. To compensate, DoorDash has live operators who can manually add ETA pads and override settings by uploading ad-hoc comma-separated values (CSV) files. Because these files have little validation or tracking, incorrect or outdated values can easily enter the mix, a problem that isn’t easily spotted or quickly fixed.
Additionally, the bulk CSV tool that these live operators originally used offered no historical evidence, forcing the ETA team to query different databases to see who made changes, what the current values were, and when they expired. This made the tool risky to use and slowed incident triage.
From bulk tools to a logistics console
As a result, we were motivated to shift from our existing bulk CSV tool to a newer internal tool that we call the Logistics Console. Uploads to the console can be validated for expiration windows and value limits, with every change fully tracked, including history, current values, who made the change, and expiration details. This metadata is visible in the console’s user interface, allowing any DoorDash employee to see changes and current states in real time.
To handle high queries per second (QPS) efficiently and safely, the console:
- Sends reads through an EntityCache keyed by area and config type, with a short time-to-live and background refresh.
- Requests fetch data in parallel with controlled concurrency.
- Protects the rollout with feature flags.
We added instrumentation to monitor cache hit rates, fallbacks, and end-to-end latency.
Overall, this migration makes configuration management safer, more transparent, and more scalable. It eliminates the need for manual database lookups, speeds up on-call response and debugging, and reduces the load on downstream systems — all while keeping latency stable and preparing for future automation and analytics.
Riding out storms
We created storm mode to manage more severe surge periods — like severe weather or major events — when Dasher supply drops and demand spikes. During these times, manually tuning ETA pads for each area becomes slow, inconsistent, and prone to error. Live operators only have a general sense of how many pads to add in fast-changing local conditions, which leads to unnecessary lateness and a noisy customer experience.
We added a storm mode toggle to the Logistics Console UI to allow our live operators to simply switch the mode on. When set to storm mode, the system automatically calculates an area's ETA pad in real-time, calculating the time based on the specific area's supply-demand ratio and distance between the restaurant and consumer. The derived pad is applied consistently across all store pages, and its status and controls are visible in the Logistics Console.
This automation removes manual guesswork for live operators, provides fast and consistent data-driven padding across all areas during disruptions, and improves on-time performance — all while keeping the system observable, safe, and easy to operate.
Auditing store ETA pads
We allow live operators to change both area-level and store-level ETAs. However, store pads can easily drift, become outdated, or be set arbitrarily. Without a feedback loop, these store pads may quietly reduce on-time accuracy (OTA) over time. In the past, it was difficult to know whether a pad was helping or hurting a store’s performance without manual data analysis — meaning bad pads could persist and delay incident response.
To solve this, we built the Store ETA Pad Auditor. It runs on a schedule to retrieve all active ETA pads and the consumer's original quoted delivery time. It then uses empirical delivery-time distributions to estimate each store’s ETA accuracy both with and without the current pad.
The auditor also tests a range of candidate pad values to find the optimal OTA pad and classifies the current one as effective, neutral, or harmful. Finally, it posts a concise Slack report with its findings and recommended actions. Operators can adjust pads directly in the console as the ETA team monitors operator behavior and pad changes end-to-end.
Overall, the auditor makes pad effects measurable and actionable, flags harmful configurations early, and closes the loop from configuration to performance to correction — making adjustments faster, data-driven, and transparent.
Impact
Migrating from ad-hoc bulk tools to the structured and observable Logistics Console has significantly improved the safety, transparency, and agility of DoorDash ETAs. With robust guardrails, real-time visibility, data-driven automation like storm mode, and the ability to make area- and store-specific modifications through the auditor, DoorDash has closed critical feedback loops and empowered operators to make faster, smarter decisions — ultimately delivering a more reliable experience for customers and Dashers alike.
Building A GenAI-Powered Voice Assistant for Verifying Store Hours
By: Anand Viswanathan
Imagine you're a restaurant owner who needs to close early because of unforeseen circumstances, for example, a holiday or inclement weather. Currently, to confirm a merchant’s hours and availability, DoorDash sends an automated phone call or robocall to the merchant, asking them to press specific numbers on a phone tree to confirm if they are open — "Press 1 for yes, 2 for no, 3 for special hours."
One limitation of the current system is that our robocalls can only be sent on the day in question—for example, the morning of a holiday—because they can’t capture detailed information or specific hours in advance. The system relies solely on a simple yes/no response, which prevents us from gathering precise schedules ahead of time.
Instead, imagine if merchants could simply respond in natural language: “We’re closing at 4 PM instead of 9 PM next Monday because of Labor Day.” With that level of detail, calls could be scheduled well in advance, and we’d have precise hours to use when disabling a store.
During my internship at DoorDash, I tackled this challenge by redesigning our robocall system to support AI-powered voice agents while maintaining full backward compatibility with the existing infrastructure.
This project involved four major components:
- Creating a flexible architecture for multiple voice AI providers.
- Engineering the voice agent prompts to extract structured data from conversations.
- Building intelligent webhooks that process natural language and trigger business actions.
- Integrating seamlessly into existing systems via asynchronous Kafka events and request flows.
The result is a system that can seamlessly switch between legacy system robocalls and AI-powered conversations based on merchant preferences and feature flags, while automatically processing real merchant responses to update store hours.
Creating a flexible architecture for multiple voice AI providers
DoorDash's original robocall system was tightly coupled to a single postal service provider, making it hard to introduce alternatives without major rework.
The goal of this refactor was to decouple the scheduler from any one vendor to make a runtime decision about which voice stack to use. By introducing an abstraction — the RobocallCreator interface — and a factory that selects an implementation per store via feature flags, we can route calls to a legacy dual-tone multi-frequency (DTMF) provider or to an AI voice agent without the caller perceiving any changes. This keeps backward compatibility intact while giving us a safe path to A/B test and gradually roll out new conversational experiences.
In practice, the scheduler calls into a stable interface. The factory then decides whether to use the postal-service pathway or an AI provider based on store configuration and flags. This isolates risk — for instance, we can roll forward or back by flipping a flag — while reducing blast radius — in other words, there are no downstream changes — and future-proofing the system for additional AI vendors. The same pattern is extended via VoiceAIAgentClientFactory so we can swap specific AI providers — for example, using Vapi today with the ability to switch to Twilio or Amazon Connect later — without touching business logic.
Designing for multiple AI providers

I created a common interface that abstracts both traditional and AI-powered systems:
interface RobocallCreator {
suspend fun createVoiceCallCheckOptOut(...): CreateVoiceCallResponseData
suspend fun createVoiceCall(...): Res<CreateVoiceCallResponse>
}
Factory pattern for provider selection
class RobocallCreatorFactory {
fun getRobocallCreator(storeId: Long): RobocallCreator {
return if (shouldUseAIRobocall(storeId)) {
voiceAIAgentService
} else {
postalServiceRepository
}
}
}
Future-proofing for additional AI providers
class VoiceAIAgentClientFactory {
fun getVoiceClient(): VoiceAIAgentClient {
return when (getVoiceClientImplementationType()) {
"VAPI" -> vapiClientImpl
// Future options: "TWILIO", "AMAZON_CONNECT"
else -> throw IllegalArgumentException("Unknown implementation type")
}
}
}
The factory pattern is the enforcement point for policy and safety: It centralizes provider selection, lets us gate by geography/merchant tier/feature flag, and ensures we always have a deterministic fallback to legacy robocalls. This is what lets us ship conversational AI incrementally — on Monday, we can run 1% of stores on the AI path, but on Tuesday, we can roll back to 0% if something looks off — while the rest of the platform keeps operating.
Prompt engineering for structured data extraction
Once we can route to an AI robocall, the next challenge is to convert an open-ended conversation into reliable, machine-actionable data. To enable this, I crafted a prompt optimized towards retrieving a structured response from the merchant that conforms to an easily processable schema.
The prompt design treats the dialogue as a user interface: It guides the merchant to state whether they’re closed, keeping regular hours, or instituting special hours, and then elicits exact times. The output must be structured so that our webhooks can act deterministically. That’s why the assistant always returns a digits field compatible with the old interactive voice response flow (1/2/3), plus normalized opening/closing timestamps when applicable. This preserves backward compatibility and keeps downstream services simple.
To reduce errors, the prompt enforces a confirm-and-repeat step (‘So you’re open from 9 AM to 5 PM today, correct?’). We also normalize time formats — for example, 5 PM or 17:00 — into a consistent schema before the webhook processes them. This combination of guided conversation, confirmation, and normalization produces dependable inputs for updating store hours automatically.
Voice agent persona: Alex
Here are examples of the prompt structure passed to the voice agent, setting up a persona for the agent as well as providing an explicit example of how to progress the conversation.
## Identity
You are Alex, a DoorDash assistant for collecting store hours.
## Flow
1. Intro: “Hi, this is Alex from DoorDash. I’m calling to verify your store hours.”
2. Ask: “Are you open regular hours, closed, or operating special hours today?”
3. Follow-up: Ask for specific times if special hours apply
4. Confirm: Repeat back to verify information
Building intelligent webhooks for business actions
Webhooks are the bridge between conversation and action. Their job is to take the assistant’s structured output, validate it, and trigger the correct business side-effects without requiring any merchants or downstream systems to change their behaviors. We designed the webhook to be idempotent, schema-driven, and strict about preconditions — for example, create special hours only when both opening and closing times are present or when digits == '3'. This keeps updates safe and auditable.
Inside the handler, we resolve the store by callId, normalize time fields, and call the SpecialHoursRepository. If the data isn’t sufficient, we avoid partial writes but still publish a status event for observability. This approach makes failures visible via Kafka while protecting source-of-truth systems from bad or incomplete inputs.
The data model for the message sent by the voice AI provider contains a myriad of other fields within the Analysis object, but for our purposes, we only need the structured data defined previously.
data class VoiceAIWebhookMessage(
val analysis: Analysis?
)
data class Analysis(
val structuredData: StructuredData?
)
Here is a snippet of the logic used to process the response retrieved from the voice agent:
suspend fun processStructuredData(
callId: String,
digits: String?,
closingTime: String?,
openingTime: String?,
voiceCallStatus: String?,
fromPhoneNumber: String?
) {
if (digits == "3" || (openingTime != null && closingTime != null)) {
getStoreIdFromCallId(callId).fold(
{ storeId ->
specialHoursRepository.createSpecialHoursForToday(storeId, openingTime, closingTime)
},
{ error -> logger.warn("Could not retrieve store ID") }
)
}
publishVoiceCallStatusUpdate(callId, voiceCallStatus, digits, fromPhoneNumber)
}
The processStructuredData function is intentionally small and composable. It validates inputs that then perform the one business mutation, such as creating special hours for today, and then publishes a status event. That separation makes it easy to extend later — for instance, to support multi-day ranges or holiday templates — without changing the voice agent contract.
Seamless integration with existing systems
To integrate cleanly, we reused the platform’s existing event model and scheduler flow. Instead of inventing new topics or APIs, the voice path publishes the same VoiceCallStatusUpdateEvent downstream. That means alerting, dashboards, and consumer services didn’t need to change. The scheduler still drives call creation, but the provider behind the interface is now different. This is how we kept the rollout low-risk and high-leverage.
Because we preserved the old contract — status updates and digits semantics — the rest of the system remains blissfully unaware of whether a call was DTMF-driven or a natural language conversation.
We can reuse the exact same call pattern as the existing robocall infrastructure, publishing the same VoiceCallStatusUpdateEvent with identical fields:
private fun publishVoiceCallStatusUpdate(callId: String, voiceCallStatus: String?, digits: String?, fromPhoneNumber: String?) {
val event = VoiceCallStatusUpdateEvent.newBuilder()
.setDdEventId(callId)
.setTimestamp(Instant.now().toProtoTimestamp())
.setVoiceCallType(...)
.setVoiceCallSubtype(...)
.setStatus(mapVoiceCallStatus(voiceCallStatus ?: ""))
.apply {
if (digits != null) digits = StringValue.of(digits)
if (fromPhoneNumber != null) fromNumber = StringValue.of(fromPhoneNumber)
}.build()
voiceCallStatusUpdateEventkafkaProducer.send(Constants.Topics.postalVoiceCallStatusUpdate, event)
}
End-to-end flow
In the new system utilizing voice AI agents, when DoorDash initiates a robocall, the following steps occur:
- Scheduler picks the provider via factory
- Voice agent "Alex" talks to the merchant
- VAPI sends structured response to webhook
- Webhook interprets and triggers special hours update in MenuDataService
- Kafka events notify systems downstream and keep stores open or closed
Conclusion
This project demonstrates how combining software engineering patterns with prompt engineering unlocks the power of generative AI in legacy infrastructure. Key strategies like using abstraction layers, guiding AI behavior via detailed prompts, and reusing existing integrations made the rollout fast, safe, and scalable.
Key takeaways for engineers include:
- Design for flexibility early: Abstract out provider logic.
- Engineer your prompts: They’re the UI of your AI system.
- Use feature flags: Safety and observability are critical.
- Integrate incrementally: Leverage existing workflows and events.
Acknowledgments
Big thanks to my mentor and the Delivery Excellence - CX Quality team for their support and feedback. Shoutout to the Menu Data Service team for partnering on integration work!
Check out more on the DoorDash Engineering Blog or consider applying to join our team!
Probabilistic ETA Prediction: Capturing Uncertainty in Food Delivery
By: Leting Ni
When customers place an order on DoorDash, one of the most important pieces of information we provide is the estimated time of arrival, or ETA, as shown in Figure 1. Traditional ETA models typically rely on either a single-point prediction or a parametric distribution, such as the Weibull distribution, which is our current approach, as described in a previous blog post. Both approaches have limitations; a single-point prediction hides uncertainty that may be introduced by traffic, merchant preparation, or Dasher availability, while a Weibull assumption may not reflect the true distribution of ETAs.
This blog post focuses on developing a non-parametric probabilistic ETA model. Using this approach, the model learns the full distribution of possible arrival times directly from data instead of assuming a specific distribution. Through exploring three modeling choices and training strategies, we identified two strong model candidates to improve accuracy while also providing deeper insight into uncertainty.

Learning distribution directly
DoorDash’s production system today assumes that ETAs follow a Weibull distribution and estimates parameters accordingly. Of course, we cannot guarantee that delivery times actually follow Weibull, which means that incorrect assumptions can hurt predictions.
To enable non-parametric learning, we redefined ETA prediction as a classification problem. Delivery durations are divided into bins — for example, 60-second intervals — and the model predicts the probability of each bin. Together, these form a probability distribution of arrival times. For this work, I have reused our standard training dataset with the date range our current production model was trained on, and included the same set of features, such as cart subtotal and available Dasher count.
To validate this approach, I performed a sanity check by comparing the distribution of predicted probabilities summed across all orders with the actual label distribution in the training set, as shown in Figure 2. The two distributions matched closely, showing that the model was truly learning the underlying data distribution rather than memorizing noise. This gave us confidence that a non-parametric, distribution-learning model was worth further pursuit.

Loss functions, point estimation, and binning
After establishing the framework, we focused on improving how the model learns and provides predictions. This work used DoorDash’s NextGen query data transformation (QDT_ architecture, developed previously by the ETA team, which is built on a multi-layer perceptron mixture of experts model with specialized encoders for different types of features. This design allows the model to capture complex spatial and temporal patterns from numerical, categorical, and time-series data, while also supporting multitask learning for related ETA outcomes. For more details, see our earlier blog posts on multi-task models, deep learning, and probabilistic forecasts and deep learning for smarter ETA predictions. Building on this foundation, this work focused on three areas: loss functions, distribution estimations, and label binning approaches.
Loss functions
Standard cross entropy (CE) treats all misclassifications equally, even if the predicted bin is close to the true one. To address this, we experimented with distance-aware losses such as the weighted sum of cross entropy and mean absolute error (CE+MAE), continuous ranked probability score (CRPS), and class distance weighted CE (CDW-CE).
The results were clear:
- CE+MAE gave the best balance, improving both MAE and CRPS.
- CRPS was theoretically appealing but did not provide good results.
- CDW-CE over-penalized distant errors, forcing distributions to be too concentrated and hurting long-tail performance.
This showed that combining CE with a lightweight distance-aware term is more effective than heavily reshaping the distribution.
Point estimation via distribution mean
While the model outputs a distribution, our current decision layer still requires a single value as input. The decision layer is the second stage of DoorDash’s ETA system, which builds on the base layer to optimize predictions for downstream business needs. You can learn more in our earlier blog post.
Initially, I used the midpoint of the most probable bin, but this approach was too narrow. Switching to the distribution mean (expected value), as shown in Figure 3, better captured the overall distribution and led to better accuracy metrics. This method ensures that downstream layers receive a representative single-point estimate while still benefiting from the richer distribution.

Binning approaches
The way we discretize time is also critical. I compared fixed-size bins ranging from 5 seconds to 120 seconds with these variable-size bin approaches:
- Quantile binning assigns smaller bins in popular ranges, balancing the number of samples per class.
- Increasing-size bins allocate wider bins for longer ETAs, because those have more uncertainty naturally.
These ideas were reasonable and, in some cases, produced results close to fixed-size bins. None, however, consistently outperformed fixed-size binning. The two best fixed bin sizes emerged as 90-second bins, which offered the best overall metrics, and 15-second bins, which had the best long-tail coverage.
Final base layer candidates
After combining these findings, I selected two base-layer candidates for experimentation:
- TRT1: CE+MAE with 90-second fixed bins, which offered the best performance on metrics. It achieved an MAE in line with the production model while improving on CRPS and the interquartile range.
- TRT2: CE with 15-second fixed bins presented the strongest long-tail performance, predicting beyond 90 minutes when needed, with good CRPS and a little degradation in MAE.
A new decision layer tuning
This next-gen architecture consists of a base layer model and a decision layer model. While the base layer produces a distribution, what ultimately matters to customers is the ETA window they see on the checkout page. The translation for this happens in the decision layer, which takes the base layer’s prediction and converts it into an ETA window. The base layer model is trained to maximize accuracy. The decision layer model can be tuned to optimize any business need, which in DoorDash’s case is.
I tuned the decision layer using a grid search over window size cutoff intervals and offsets. Guardrails were essential: New configurations must not degrade critical performance metrics like “early,” “late,” and “20-minutes late (20mLate),” and also must keep window size and the midpoint reasonable within a small buffer.
As shown in Table 1, both candidates showed either improvements or parity across on-time arrival (OTA), Early, Late, 20mLate, and Average Midpoint. The only tradeoff was a slight increase in average window size, but that remained within acceptable buffer ranges. Importantly, this means the probabilistic models can be deployed without compromising reliability, while giving us richer information about uncertainty.

Next steps
Following a power analysis, I launched these two treatment models in a production A/B test with 100% exposure and a plan to hold for two weeks. A preliminary read of the results reached a statistically significant improvement in OTA and earliness with no significant degradation of other metrics. If these results remain steady, one of these models will be adopted as our champion production model.
Conclusion
Traditional ETA models fail to fully capture uncertainty on an order level. By reframing ETA prediction as a non-parametric probabilistic task, we can learn the entire distribution directly from data.
Through experiments with loss functions, point estimation strategies, and binning, we identified two strong candidates that balance accuracy and long-tail performance. Both integrate smoothly into the decision layer and show improvements in critical ETA metrics.
This project demonstrates how probabilistic modeling can make ETAs not only more accurate but also more transparent and trustworthy for customers. These principles potentially could be extended by optimizing the decision layer to search for a window that maximizes the area under the curve in the predicted distribution.
This work is from a summer intern project by Leting Ni, with Jason Kim and Pierre Counathe as his mentors, and the ETA team’s guidance and support. This project showed how probabilistic approaches can push ETA modeling forward by capturing uncertainty, which is an essential part of real-world machine learning.
Personalized alcohol recommendations with LLMs
By Maxine Wu
On DoorDash’s Alcohol Landing Page, customers can browse ranked store lists, store carousels, and category tiles, but there are currently no personalized item carousels. This limits product discovery. Our goal was to introduce personalized alcohol recommendations to the Alcohol Vertical Landing Page by leveraging Large Language Models (LLMs) to power in-app item carousels.
Leveraging LLMs for recommendations
Large Language Models (LLMs) open up a new way to provide personalization and offer human-interpretable recommendations that align with users’ preferences and previous order history. This enables us to generate “Why You’ll Like This” explanations (for email notifications) and in-app carousel titles to make the carousel more engaging. LMs also allow us to scale personalization in domains that historically lacked the data density required for traditional recommendation systems. For alcohol recommendations specifically, LLMs can use learned representations of beverage categories and attributes to generalize from limited user behavior and produce meaningful suggestions.
Our current LLM-powered recommendation pipeline generates item-level alcohol recommendations by taking in a user’s order history and search terms, using semantic search to surface real, orderable products from the DoorDash catalog, and writing the results to a database. During my summer internship, I helped build an in-app carousel that fetches and displays these recommendations.
Leveraging frameworks for carousel development
To build the new carousel, we used DoorDash’s Carousel Serving Framework (CSF), Discovery SDK, and Vertical Landing Page (VLP) Layout Framework. CSF is a page-agnostic system for defining new carousel types. Using CSF, we created a carousel type and a content fetcher that retrieves recommendation data from the database.
CSF works with the Discovery SDK, which defines how products - such as carousels, banners, and other components - are assembled into the final feed. This separation allows each feature to maintain its own latency budget. Each product, including the alcohol recommendations carousel, defines a product service that is added as a dependency to the New Verticals (NV) Destination Pipeline, which powers the VLP. Finally, using the Layout Framework, we mapped the alcohol recommendations placement type to the appropriate carousel configuration, ensuring the correct placement in the UI.
Combining these frameworks with server-driven UI, engineers can quickly create new carousel types, focusing on content rather than presentation. The built-in observability from Discovery SDK and CSF (such as job-level metrics and error logs) also simplifies debugging and iteration.
Leveraging LLM Creativity: Generating engaging carousel titles
Along with leveraging LLMs for recommendations, we use it to generate the carousel titles. For this, we developed a two-stage pipeline:
- Candidate Generation: The LLM proposes 3–5 short (3–5 word) titles based on the recommended items and its own analysis.
- To reduce irrelevant or misleading titles, we introduced a chain-of-thought-style prompt: the LLM first summarizes the beverage categories in the carousel (e.g., red wines, beers, seltzers), then creates a title that captures them all (ie. "Smooth Reds & Easy Sips"). This step prevents the model from generating misleading titles such as “Unwinding with Wine” when the carousel includes drinks other than wine.
- Title Judge and Re-Rank: Because the first suggested title was not always the most natural or accurate, we added a second step where the LLM evaluates its own candidates. It filters out overly editorial or generic titles and re-ranks the rest. This reliably produced stronger, more contextually accurate titles.
Key learnings
During this project, I learned about the importance of solving concrete problems rather than overgeneralizing too early. By focusing on a specific case (ie. recommending alcohol products), I was able to dive deep and look at the tail end (ie. when the generated titles were too vague/broad) to make targeted improvements. I also learned the importance of observability and metrics; without clear signals into performance errors, it’s hard to troubleshoot and debug effectively.
Conclusion
The launch of our alcohol recommendation email and early employee testing for the alcohol recommendations carousel demonstrate how LLMs can drive meaningful personalization in the Alcohol VLP. By grounding recommendations in a user’s order history and pairing them with interpretable, engaging carousel titles, we make discovery feel intuitive and delightful. More broadly, this work highlights how LLMs enable high-quality personalization in domains that were historically underserved due to sparse data.
Huge thank you to the New Verticals Growth team - across the in-app and notification pods - for their guidance and support throughout this internship. This milestone wouldn’t have been possible without them - congrats team!
Enhancing the Dasher Lateness Contract Violation Dispute Experience
By Vedant Jolly
Trust is paramount in the on-demand economy. For the millions of Dashers who partner with the DoorDash platform, the speed and fairness of our operational processes are essential for maintaining that trust. DoorDash’s legacy system for handling Dasher lateness disputes was a manual, offline process that created critical delays before a case could even be reviewed. This post details how we rearchitected this entire workflow from the ground up, migrating it from a slow batch system to an intelligent, real-time pipeline. By doing so, we reduced the processing time from 24+ hours to just a few seconds, significantly improving the Dasher experience and creating a more scalable, efficient system for our internal operations teams.
The problem: Hidden costs of a processing delay
The previous system for handling lateness contract violations (CVs) was a significant source of friction both for our Dashers and our internal teams. It was built on an offline batch process to identify and process new disputes. This architecture created several cascading problems, including:
- A poor Dasher experience: The most critical issue was the hours-long lag between a Dasher submitting a dispute and an agent beginning to review it. This delay led to a period of uncertainty and anxiety for Dashers. More importantly, a pending CV could temporarily impact their timely access to key earnings programs, including “Earn by Time” or even their Top Dasher status.
- Operational and engineering inefficiency: From a business perspective, the old system was a major bottleneck. It was inflexible, making it difficult to update our dispute logic or roll out enhancements. This rigidity created significant operational overhead, tying up resources in manual, repetitive tasks and preventing the team from focusing on more strategic, high-impact work.
The solution: Architecting an intelligent, real-time pipeline
To solve these problems, we designed and built a new, real-time pipeline from scratch. The goal was to create a system that was not just faster, but also smarter. The new workflow is centered around a central decisioning service, which acts as the brain of the operation.
The new architecture: When a Dasher submits a dispute now, the mobile app calls a new API endpoint in a backend service, which is responsible for orchestrating the entire real-time workflow, from classification to case creation, as shown in Figure 1:

Intelligent routing and prioritization: The central decisioning service contains the core business logic that was previously trapped in offline SQL queries. It instantly classifies a dispute based on various metadata signals. For certain disputes, I implemented a system that uses historical data to prioritize cases. This means a case is created only if a Dasher's history meets certain criteria, allowing our operations teams to focus their attention on the most critical issues first.
Closing the loop with automated rerouting: We also accounted for complex edge cases. I built an automated workflow that is triggered when an agent overturns an initial dispute. This workflow re-evaluates the Dasher's history against our quality thresholds and, if necessary, programmatically creates a new case in the appropriate queue. This ensures that even if a Dasher is cleared of one issue, a persistent underlying issue will still be addressed.
Ensuring reliability at scale
Launching a mission-critical, real-time system at DoorDash's scale requires a relentless focus on reliability. As part of this project, I was responsible for identifying and fixing critical bugs and building a robust monitoring strategy.
Solving for idempotency: During testing, we discovered a race condition that could cause creation of duplicate dispute cases if a Dasher tapped the submit button multiple times. I traced this issue across multiple services and implemented idempotent logic at our backend API layer, guaranteeing that only one case is created per dispute.
Building a monitoring and alerting strategy: To ensure the long-term health of the system, I built a new monitoring dashboard to provide real-time visibility into key success and error metrics. I also implemented critical alerts that notify the on-call engineer of any potential issues, such as a drop in the case creation rate or a spike in errors, before they can impact Dashers.
Conclusion
The migration of our lateness dispute process from batch to real-time has had a significant and immediate impact. We achieved a 99.9%+ reduction in processing delay, taking case creation time from more than 24 hours to mere seconds. This has vastly improved the Dasher experience with faster resolutions while reinforcing trust in our platform. From an engineering perspective, it has given us a scalable and agile foundation upon which we can quickly build and iterate in the future. This project serves as a powerful reminder that investing in operational efficiency and user experience is not just a nice-to-have; it's a critical component of building a healthy and scalable platform.
Acknowledgements: I'd like to thank my mentor Wenting Yeh, my manager Ling Lin, and the entire Marketplace Fraud Engineering team for their guidance and support throughout this project.
