5 Lessons from Building a Delivery App using AWS

Design “takeaways” from designing for takeaways

Delivery apps were and are quite the rage, with many startups attaining escape velocity by following the “Uber for X” script across sectors, be it groceries, medicines and whatnot.

Quite recently, I got the opportunity to try out a fun exercise of building the back-end infrastructure from scratch for a delivery aggregator platform.

Delivery in the time of COVID (Credits: Photo by Alex Mecl on Unsplash)

The Problem

Before that, it helps to identify the main features of any delivery app and what distinguishes it from other systems.

  1. Highly Variable Workload: Delivery apps are among the most challenging to design as they face a constant barrage of requests. Depending on the vertical, loads can peak during specific time windows in a day (food delivery apps) or during specific days in the year (holiday season gifts). Also, requests shut off once night falls in some sectors.
  2. No margin for error: Missing requests is not an option in the competitive world of logistics. Also, it is difficult to evaluate how critical a specific parcel is for the client and losing customers early on does not bode well.
  3. Regular State Updates: Maintaining logs of package states is the norm and requires both large database storage capacity along with high throughput.

On top of this, our specific solution desired to preserve expansion to newer delivery segments and include new attributes depending on functionalities, thus forcing us to go schema-free.

The problem statement also assumes we are “hyper-local”, or stated more precisely for our purpose, maintaining sub-kilometre level location data. Thus, accurate estimates of delivery agent locations were required and could be achieved with a steady stream of location measurements.

The Solution

Since the main requirement was catering to the variable workload, we decided to go with a “serverless” architecture and AWS was chosen as the platform of choice owing to its popularity along with a free tier that lets us perform small-scale tests with little to no cost.

Building the first cut was quite literally done overnight owing to the simplicity of modern cloud services. The system design, however, was much more involved and some of the learnings from it are as follows:

  1. Start with Lambda, API Gateway and DynamoDB: It might feel daunting to start off building a critical, large-scale system. But it goes a long way in reminding oneself that most applications can be broken down into:
    a. Micro-services performing small, well-defined tasks that need to be highly scalable (Lambda)
    b. APIs for services to talk between themselves (API Gateway)
    c. Persistent storage based on the usage of a schema (DynamoDB)
  2. Use Mapping Templates: Avoid excessive usage of functions, especially when the only transformation involved is reformatting the input request. Perhaps you would like to retrieve data from DynamoDB and you will save a ton by using mapping templates, as opposed to computing and storage costs through the usage of Lambda.
  3. DynamoDB can operate as strongly consistent: Many people might be under the misconception that NoSQL databases do not provide ACID-level guarantees. Although not the default option, DynamoDB can also be used in a strongly consistent fashion. This was a game-changer and allowed us to build a schema-free solution that provided accurate driver state, as double-booking a driver was a strict no-no. This held up to the test even when requests were microseconds off from a trigger.
  4. Maintain a separate database for location updates: Despite the large number of driver status changes that one will encounter in a delivery app, the number of requests is dominated by the constant deluge of location updates. Most trips will have at most 5–10 state changes associated, but there are far more location updates. Also, location data has a defined schema, thus making a standard SQL database an excellent choice. Make sure to have a data API along with the DB to prevent additional costs for a gateway.
  5. Use the AWS Calculator: Cost is a useful metric in arriving at choices during system design. Calculator.aws provides a clone of the interfaces used to summon various AWS services and gave confidence to the business team that the cost per delivery was within margins.

Many thanks to Ajit Kumar Voraboina and Srinadh Nidadana for discussions related to this post.

Blogging about data, systems and ML