- Published on
The Hidden Complexity of Optimizing Delivery Range Validation in Location-Based Services
- Authors
- Name
- Sedky Haider
When building location-based services, one of the most deceptively complex problems is determining whether a customer is within your service area. What seems like a simple yes/no question actually involves multiple systems, geographic calculations, and business logic.
Let's explore how 1StopLaundry solves this problem from an on-demand laundry service application to understand why this is such a challenging problem.
The Seemingly Simple Question
On the surface, checking if a customer is within your delivery range sounds straightforward:
- Get the customer's address
- Check if it's within your service area
- Return yes or no
But as we'll see, the reality is far more complex.
Breaking Down the Problem
Looking at the entry point function, we can identify several layers of complexity:
1. Address Validation and Geocoding
// ... existing code ...
const geocodeData = await geocodeAddress(user.address);
if (geocodeData.status !== "OK") {
return res.status(400).json({ message: "Invalid address" });
}
const customerLocation = geocodeData.results[0].geometry.location;
// ... existing code ...
Before we can even check if an address is within range, we need to validate that it's a real address and convert it to geographic coordinates. This requires:
- Integration with external geocoding services (like Google Maps)
- Handling various address formats and edge cases
- Managing API rate limits and costs
2. Finding Nearby Service Locations
// ... existing code ...
const foundLaundromats = findLaundromatsWithinMaximum(customerLocation);
const outsideMaximum = foundLaundromats.length === 0;
// Sort by distance closest to user.
foundLaundromats.sort((a, b) => a.distanceKmToUser - b.distanceKmToUser);
// ... existing code ...
Once we have coordinates, we need to find service locations within range. This involves:
- Calculating distances between points on a sphere (Earth) using Haversine formula
- Determining which service locations can serve the customer
- Sorting by proximity to optimize for efficiency
Which Service Provider To Show?
The customer doesn't care about the distance to the service provider, as we do their pickup and dropoff, they care about the time it takes to get their laundry done. Further, they don't care about which service provider they get, as long as it's done..
- Do we show the closest service provider?
- Do we show all service providers within range?
- Do we show the service provider with the most availability?
- Do we show the service provider with the most capacity?
- How do we handle cases where we have multiple service providers within range?
If merging availability across service providers, we need to think about:
- If a customer selects a time which is offered by multiple service providers, which one do we assign to?
- If a service provider is out of standard range, there is a fee. How do we balance which availability blocks to show in this case, standard range vs out of range?
- How do we handle cases where we have multiple service providers with the same availability?
- How do we handle cases where we have multiple service providers with the same availability and distance?
3. Multiple Range Determination Methods
The function uses two different methods to determine if a customer is within range:
// Even if the customer is within range, we need
// to check if the Driver Fleet can support this route.
if (
closestLaundromat && closestLaundromat.range
) {
const customerAddress = formatAddress(user.address);
const quote = await getQuote(
customerAddress,
foundLaundromats[0].address,
addHoursToDate(new Date().toISOString(), 24)
);
// If quote is null, ie the quote failed, then we cannot support this route.
// overwrite the range here, as we use FLEET API to determine, instead of Haversine
if (!quote?.id) {
userPatch.address.range = "OUTSIDE";
// ... existing code ...
} else {
userPatch.address.range = "WITHIN";
}
}
// ... existing code ...
This shows:
- An initial check using a simple distance calculation (likely Haversine formula)
- A secondary check for certain cities using a fleet service API
- Different business rules depending on the city
- We don't check the quote for NOW, as NOW's circumstance won't look like tomorrow.
4. Data Collection for Business Intelligence
// Save the user's address and the distance from the closest laundromat
const userPatch: Partial<Customer> = {
address: {
...user.address,
distanceKm: outsideMaximum ? null : foundLaundromats[0].distanceKmToUser,
range: outsideMaximum ? "OUTSIDE" : foundLaundromats[0].range,
},
email: user.email,
firstName: user.firstName,
lastName: user.lastName,
};
// ... existing code ...
The function doesn't just determine eligibility - it also collects data on customer locations to inform future business decisions, even for customers outside the current service area.
This helps us to visualize clusters of customers in order to plan for future expansion.
5. Availability Checking
// if inside range, let's add availabilities to the response
const cleanerIds = foundLaundromats.map((c) => c.id);
const availabilities = await getCleanerAvailabilities(cleanerIds);
if (availabilities.length == 0) {
logAndSendNotif(
`No availabilities found for customer: ${req.user?.uid}. \nCustomer Address: ${user.address.addressLine1}, ${user.address.city}`
);
}
response.availabilities = availabilities;
// ... existing code ...
Being within range isn't enough - the service also needs to have available capacity at the nearest locations.
Why This Is Hard
The complexity of delivery range checking stems from several factors:
1. Geographic Complexity
Earth is not flat, and calculating distances accurately requires spherical geometry. Different distance calculation methods have different trade-offs between accuracy and performance.
2. Business Rules Complexity
What defines "in range" varies by:
- City or region
- Service type
- Time of day
- Current capacity
- Delivery partner availability
3. Data Quality Issues
Customer addresses can be:
- Incomplete
- Incorrectly formatted
- Ambiguous
- New developments not yet in mapping systems
4. Integration Complexity
The function integrates with multiple systems:
- Geocoding services
- Fleet/delivery services
- Internal availability systems
- Customer databases
- Notification systems
5. Performance Considerations
Range checking needs to be fast enough for a good user experience, but accurate enough to prevent operational issues.
Conclusion
What appears to be a simple geographic check is actually a complex business process that touches multiple systems and incorporates various business rules. The function demonstrates how even seemingly straightforward features in location-based services require sophisticated implementations.
For businesses building similar functionality, it's important to:
- Consider multiple methods of range determination
- Collect data on edge cases for future expansion
- Build in notification systems for human intervention
- Balance accuracy with performance
- Account for the unique characteristics of your service area
By understanding these complexities, you can build more robust location-based services that accurately determine service availability while providing valuable business intelligence.
The technology driving these business decisions is not as important as the business logic driving these decisions. While having robust technical implementations is crucial, the key to success lies in properly understanding and implementing the business requirements.