An Open-Source DoorDash Clone? Lucine Pt. 2

Introduction
In the last article, we discussed the importance of starting with a high-level overview of the project and working out the details from there. We also talked about how this is related to the concept of "abstraction" in engineering and why thinking about abstractions is useful for designing, communicating, and building out our systems.

For the rest of this series, we will focus on fleshing out our requirements document. We will do this by looking at each high-level idea and asking "why" and/or "how" until we feel that we have a sufficient level of understanding/detail. In this article, we will focus on the first high-level requirement: "Allow customers to request orders for deliveries on our platform". As a refresher, this is where we left off in the last article:

  • Allow customers to request orders for deliveries on our platform.
    • Customers select their desired items from our user interface.
    • When payments are successful, the user gets their receipt and an ETA.

So our goal is to ask the following two questions:

  1. "How do we let customers select desired items?"
  2. "How do we give users receipts and an ETA when payments are successful?".

From there, we will continue asking "how" and "why" until we feel we have a solid grasp of what we need to do when we begin programming/developing.

We can answer question (1) by thinking about how we want to give users access to the platform. There are multiple ways to do this: desktop software, mobile app software, web applications software, etc. We want users to be able to use a wide range of devices to place their orders, so a web application is an immediate and obvious good first choice. We also want to target as wide an audience as possible and therefore creating a mobile application is also an obvious choice.

So, to answer question (1), we can say: "Customers can go to our website and place orders or they can use our mobile app." I briefly spoke about how this may not seem like that big of a "clarifying" step compared to what we already had--I would agree with this sentiment. So, how about we go a step further and also ask:

  1. "How will the website work?"
  2. "How will the mobile app work?"

Starting from a single high-level requirement and asking "why" and/or "how", recursively, will always give us a direction to start and a path to walk down when designing a system--which is fantastic, because sometimes it is really hard to know how to start when designing something from scratch.

Talk About Abstraction!
Let's answer question (3): "The website will work by presenting restaurants to the user that the user can order from. When a user selects one of these restaurants, they are presented with a menu that they can use to build up their order. After they have selected their desired items, they will be taken to a "checkout" page that they can then use to pay for and place their orders." Notice how this is a significantly more involved answer than we've had to provide before. A general rule of thumb is that if your answer requires more than a paragraph to explain a single question, you may be going too far down into the concrete details of "implementation"--in other words, you could probably answer the question in a shorter, more concise fashion by answering using a higher level of abstraction.

SIDE NOTE: Why are we trying so hard to not answer these questions with low-level details? The reason is that thinking about low-level details make it hard to see how a system can change if it needed to. Again, let's use an analogy in electronics/circuit design. there are many, many different kinds of transistors available for purchase in the market. Each one has different low-level, specific-to-the-type properties, but no matter the difference in these low-level characteristics it still does what a transistor needs to do. So when we're building a circuit by following its schematic and come across the symbol for a transistor, it doesn't matter what these low-level characteristics are: we can more or less use any transistor we have on hand. For programming, ignoring the lowest-level details helps us ignore whether we design something in Python or Rust and instead just use whatever language/library is the most efficient/effective for what our high-level requirements are trying to do.

Let's update our requirements document with the answers we've gathered so far and continue asking questions from there:

  • Allow customers to request orders for deliveries on our platform.
    • Customers select their desired items from our user interface.
      • Customers can place orders on our web application.
        • The website will work by presenting restaurants to the user that the user can order from.
        • When a user selects one of these restaurants, they are presented with a menu that they can use to build up their order.
        • After they have selected their desired items, they will be taken to a "checkout" page that they can then use to pay for and place their orders.
      • Customers can place orders on our mobile app.
    • When payments are successful, the user gets their receipt and an ETA.
  • Gather and process payments from customers for their orders.
    • Customers are charged based on the expected time to completion of their orders.
  • Reach out to restaurants and place food orders on behalf of customers.
    • When an order is placed, we want to call restaurants and place orders.
    • If applicable, we should use APIs to place orders electronically.
  • Send paid customer orders to third-party contractors (delivery drivers).
    • Drivers have a mobile app they can log in to and receive orders.
    • Drivers can accept or decline any order sent to them.
  • Keep track of orders and order state, as well as store previous order history.
    • Orders can be “pending”, “active”, “completed”, “refunded”, or “canceled”.
    • We want to use some kind of database to store the date placed, the date completed, and info about which driver delivered each order.
  • Pay drivers when orders are completed.
    • Money that we take from customers needs to be buffered to pay drivers.
    • Drivers get paid as a function of distance and time.

We now have the following questions to answer:

  1. "How does the website present restaurants to the customer?"
  2. "How do present the menu to the customer for a restaurant?"
  3. "How does a user pay for their orders on the checkout page?"

Notice that question (7) is related to a very high-level requirement in our overall design: "Gather and process payments from customers for their orders." This is where we can decide to ignore or modify question (7) since it will be answered later on in the document, or instead remove the high-level requirement and just answer question (7) since the same information will be present if we do.

I prefer leaving the high-level requirement and ignoring question (7). The reason for this decision is that we have decided that payment processing is a "central pillar" of the application and thus it should be right there, front and center at the highest level. Making it a low-level detail, in this context, muddles the direct communication of our intentions about what we want if/when we talk about our system with another person. Also, there is no good way to modify question (7) to where it addresses a detail that won't also get addressed in the other high-level analysis, so we just ignore it altogether.

Beginning of Implementation Details
To finish this article, we are going to answer questions (5) and (6). The answer to question 5 will be: "The website will store all partnered restaurants. We will develop UI components that are intuitive for our customers and let them click on each restaurant and see details about how close the restaurant is, the rating for the restaurant from other customers, and a link to the restaurant's menu."

The answer to question (6) is very similar to question (5): "The menus will be displayed by storing menu items for each restaurant and finding some way to link the menu to the restaurant we are also storing. Each item for the menu can be interacted with to add menu items to a list to keep track of the customer's order. Menus will only be accessible from the restaurant's page that the menu belongs to."

We are starting to talk about UI design and developing "components" at this level of detail. We can expect that in the next round of questions that we aim to answer, we will have to talk about concepts that border on programming details or otherwise details relating to the act of programming the UI. If we have done our question/answer process correctly, this should "feel" like a smooth, gradual transition into such complex details. In the next article, this is where we will start talking about programming and begin developing the application! Before we finish, however, let's update our requirements document:

  • Allow customers to request orders for deliveries on our platform.
    • Customers select their desired items from our user interface.
      • Customers can place orders on our web application.
        • The website will work by presenting restaurants to the user that the user can order from.
          • The website will store all partnered restaurants.
          • We will develop UI components that are intuitive for our customers and let them click on each restaurant and see details about how close the restaurant is, the rating for the restaurant from other customers, and a link to the restaurant's menu.
        • When a user selects one of these restaurants, they are presented with a menu that they can use to build up their order.
          • The menus will be displayed by storing menu items for each restaurant and finding some way to link the menu to the restaurant we are also storing.
          • Each item for the menu can be interacted with to add menu items to a list to keep track of the customer's order.
          • Menus will only be accessible from the restaurant's page that the menu belongs to.
        • After they have selected their desired items, they will be taken to a "checkout" page that they can then use to pay for and place their orders.
      • Customers can place orders on our mobile app.
    • When payments are successful, the user gets their receipt and an ETA.
  • Gather and process payments from customers for their orders.
    • Customers are charged based on the expected time to completion of their orders.
  • Reach out to restaurants and place food orders on behalf of customers.
    • When an order is placed, we want to call restaurants and place orders.
    • If applicable, we should use APIs to place orders electronically.
  • Send paid customer orders to third-party contractors (delivery drivers).
    • Drivers have a mobile app they can log in to and receive orders.
    • Drivers can accept or decline any order sent to them.
  • Keep track of orders and order state, as well as store previous order history.
    • Orders can be “pending”, “active”, “completed”, “refunded”, or “canceled”.
    • We want to use some kind of database to store the date placed, the date completed, and info about which driver delivered each order.
  • Pay drivers when orders are completed.
    • Money that we take from customers needs to be buffered to pay drivers.
    • Drivers get paid as a function of distance and time.

See you next time!

Special thanks to Trevor Darley for proofreading this article