An Open-Source DoorDash Clone? Lucine Pt. 1
Introduction
Greetings, everyone! I'm Joshua Walton, the founder of Strong Force Solutions, a company specializing in software development and education. Allow me to take you on a journey back in time: a few years ago, I embarked on my software development adventure, and my very first significant project was a DoorDash and UberEats clone. Fast forward to the present, and I find myself with a strong desire to start from scratch, armed with the knowledge and experience I've accumulated along the way.
Let's talk about those big delivery companies for a moment. In my younger days, I was a frequent DoorDash user, as were many of my friends. However, we all came to a collective realization. The tactics employed by the industry giants are, to put it mildly, manipulative, predatory, and dehumanizing.
So, here's the plan: I intend to rebuild my initial platform entirely as an open-source solution–let’s officially say hello to Lucine, our delivery platform. Furthermore, I'm committed to providing a comprehensive course on its development in the form of these articles.
But here's the twist: I won't simply be delivering writing content. Each article will be complemented by a corresponding video on YouTube, adding depth and clarity to the material. Think of these articles as the foundational knowledge, and the videos as the more comprehensive iterations.
Now, a word of caution: due to the dual approach I'm taking to create this content, please be aware that it may take some time between each new upload. Rest assured, the wait will be worthwhile. Join me for a journey that promises to be educational, enlightening, and, most importantly, a testament to the power of open-source software development.
Requirements Document
As with any good engineering project, we should start first by listing out the most important things we can think of that our platform should set out to achieve. This doesn’t need to be in-depth, complicated, or technical at this level of abstraction. Our goal with this high-level overview is to simply begin a corpus/body of knowledge we can use to maintain our focus and refer back to when we get confused about what to do next.
I implore the viewer/reader to stop and spend some time thinking about what they themselves would consider to be the most important things a DoorDash/UberEats clone needs to implement to be “functional”/”complete”. It will be good to take every opportunity in this series to stop and do what I am about to do before I do it so that you may compare your intuition/understanding with my own. That will enable you to get the most out of this series.
After spending some time thinking, here is the very high-level list that I came up with: \
- Allow customers to request orders for deliveries on our platform.
- Gather and process payments from customers for their orders.
- Reach out to restaurants and place food orders on behalf of customers.
- Send paid customer orders to third-party contractors (delivery drivers).
- Keep track of orders and order state, as well as store previous order history.
- Pay drivers when orders are completed.
These are the central “pillars” of any delivery platform in the industry. Any less than these bare requirements (arguably, payment processing is not required) and we won’t really be building what we want to build. We will continue to refer to them as the central pillars of our application and use them extensively when we begin to design/model our software.
Now, let’s go back through this list and deal with each “pillar” in turn. We want to come up with some next-level details just below the “main idea” level of abstraction. We will start with the ability to let customers place orders.
- 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.
- 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 login 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, 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.
Why Use Abstractions?
Let's emphasize the importance of clarity in this step. Specifically, referring to our first sub-point, "Customers select their desired items from our user interface," when delving deeper into this abstraction, it's essential to ask questions like "What kind of user interface?" In our case, we intend to offer both a web application and a mobile app, which can collectively be termed the "user interface."
This seemingly minor detail highlights the interplay between abstraction and concrete implementation. To transition from abstraction to implementation, one must continually inquire by asking "why," "how," and "what" until a satisfactory understanding is reached. In software design, it's challenging to pinpoint when an abstraction becomes either "too abstract" or "too concrete" for its original purpose.
Abstractions are commonly employed to elucidate functions to others or to simplify comprehension of complex systems. An analogy can be drawn from other engineering fields, such as electrical engineering, where circuit diagrams, or schematics, use symbols to represent physical components manipulating electricity.
So, how does this relate to software and abstraction? Consider why circuit diagrams are valuable. These diagrams facilitate the design of entire electronic systems by abstracting complex semiconductor physics into simplified assumptions and symbols. This abstraction enables the creation of various devices without delving into the intricate behavior of individual components.
Abstractions allow us to overlook intricate details, enabling a focus on higher-level aspects. Complex components, like transistors, are simplified behind abstractions, liberating us to concentrate on constructing more intricate systems, such as computers, robots, and clocks.
In software development, it's advantageous to embrace an "abstract-first" approach. Given the multitude of programming languages and paradigms available, abstracting high-level concepts guides us in adapting our implementations without altering the core application.
Moreover, effective communication with non-technical stakeholders is crucial. For instance, when seeking funding from investors, explaining the software's functionality may be necessary. The approach of starting with abstractions and gradually delving into concrete details enables us to provide comprehensive explanations layer by layer, effectively addressing questions and concerns.
Conclusion
As this is the very first part of this series, I truly hope that I met the level of quality I have set out to achieve. There is a lot to cover with this series, and I truly will not have the time to cover every single detail. That being said, I would love to see comments and feedback about this article so I can refine future content for clarity and quality.
If you enjoyed this, please consider sharing the article with your friends/other developers and subscribe to my channel/newsletter. I will make this series regardless, but the level of engagement and support I get from this series will let me know whether it’s something I want to continue putting this much effort into for another series and/or other content.
Let’s learn even more next time!