Read about my journey designing and developing custom full-stack software to alleviate the growing pains of a $30M delivery startup.
Duffl is a convenience store delivery service startup for college students boasting a delivery to doorstep time of 10 minutes. The company participated in Y Combinator’s W2020 batch and raised $12M in Series A funding back in 2021. Currently, Duffl operates at 7 locations across 3 states. But long before the establishment of large warehouses and streamlined systems came humble beginnings.
Upon the arrival of shipments, each product underwent manual assignment of inventory count and location. Locations included shelves, fridges, and freezers of varying sizes which posed space constraints issues. We determined future shipments based on the number sold in the previous week and naively updated inventory after restocking and sales. As far as the restocking process, a restocker would consult a central inventory page, where inputting a product name would yield a product photo, inventory amount, and locations to which it belonged.
The earliest stages involved Costco runs and only a few storage rooms with shelves.
The inside of our second location, where more space meant more problems...
As an engineering team, we agreed that a scalable solution lies in technology. To ensure a robust product that addresses the correct issues, I employed various research techniques to ensure user-centric design.
Surveys were conducted among 35 warehouse managers and restockers, out of which 27 responded. The objective was to identify pain points in the restocking and buying process. Participants were incentivized by being awarded a $10 order discount on completion. The survey was administered through Google Forms and data was organized and analyzed on Miro. Warehouse managers handled the buying process and restockers handled restocking, however, since most employees have experience on both sides the questions were not specific to a particular role. Questions were designed based on the challenges listed above and meant to collect both quantitative and qualitative data.
Qualitative responses were grouped by the needs they represented and color-coded by the question they came from
I created a wireframe on Figma that addressed each of these newly discovered needs.
Wireframes for the buying and restocking flows that I presented to the team.
To evaluate the usability and effectiveness of the design, I conducted a moderated user testing session where participants consisting of warehouse managers and restockers were instructed to “click” through the flow by physically interacting with the printed prototype.
Following successful usability testing, we were ready to build a UI that presents warehouse managers with visibility and management options for each product. Within this implementation, we would also reduce points of friction from the existing buying/restocking process and empower users to make informed decisions by offering algorithmically based recommendations along the way.
The completed view of Inventory UI for product tracking and management, designed and implemented by Elliot Fouts.
The Buying tab supports an option to generate a new order based on the supplier, expected delivery date, and new restock date. A backend algorithm, detailed later in this post, puts together a recommended order for the warehouse manager to place with the supplier. The warehouse manager can make changes they see fit or opt to start a blank order from scratch. This feature reduces user error by taking the legwork out of thinking and creates an order representation for us to track in our system.
The process of generating an order which a warehouse manager can later edit and place.
Clicking into a restock order on any of the tabs will take you to a table view of the order itself complete with all the items and details. Because this view is primarily used for facilitating the active restocking process, I built it with a responsive design in mind. The flow is as follows:
In the original hierarchy, some tables were placed lower down in the component tree than the input field. As a result, the table element would rerender leading to a flickering experience every time a user typed into the search bar. The solution was to anchor all modals to the topmost level of the application and control toggling through React context.
A few months after release, a ticketing system was introduced that would make a polling API call in the background of the admin application every few seconds. This call would trigger a rerender of the restock table and cause a flicker. By wrapping the table render function with a React.memo(), I successfully prevented rerendering when no props were changed. While certainly not everything should be memoized, this was a suitable use case that resulted in a bug fix.
Our backend is built on top of the Django framework and interfaces with a MySQL database. We aimed to implement an intelligent system to guide warehouse managers in determining the optimal quantity of products for each order. Achieving this would reduce waste, cut down on out-of-stock items, lower rates of human error, and overall contribute to a more efficient and streamlined ordering process.
The first step requires calculating an item’s rate of sale (ROS). This is determined by # of units sold per hour it is available for purchase. To figure out the amount of time an item is live over 24 hours, I looked at “In Stock”/“Out of Stock” and “Store Open”/“Store Close” markers as depicted below.
The highlighted areas on the top line represent the time frame in which an item is considered available for purchase.
Combining ROS with time between restocks gives us the total number of units needed in our store. Factoring in current inventory and the amount already purchased gives us the number of units we would need to purchase within an order.
# units to purchase = # units needed - # already purchased - current inventory
Divide by pack size and round up and we get the number of packs to order.
When a user generates a new order from the Buying tab, a post request is sent to the backend with the supplier name, expected date, and next restock date. In the backend RestockView, we fetch all active products from the store of the given supplier. We get pack size and price from a previous RestockItem. Then, we create a new RestockItem instance for each item in the order then assign them to the new RestockOrder. Finally, we return the ID of the RestockOrder and a paginated list of RestockItems with product info, recommended # packs, and pack cost for the UI to display.
Calculating the rate of sale for all items proved to be a time-intensive process. Originally we performed these calculations as an asynchronous task in Django Q. However, Django Q was coupled to our web server and would end up competing for CPU and memory with other application functions. To optimize for performance, I offloaded the ROS calculation task to RabbitMQ + Celery and a cron job.
As a result of better tracking and inventory management, we achieved a lower overall out-of-stock rate across all products. The improved organization allowed us to maintain low pick and pack times even as order volume increased. The ability to input and view expiration dates as a part of the restocking process reduced waste from expiring perishables. We significantly reduced the number of steps necessary to replenish products while at the same time significantly cutting down on user error. All said and done, we were able to achieve volumes of 700+ orders per day.
To successfully program software requiring as much user input as this one, I needed to spend time developing away from the computer. Many hours spent in the warehouse crowdsourcing feedback and getting my hands dirty led to numerous improvements in this feature over time and certainly added to a sense of reward upon completion. This combined with the opportunity to embed technology into one of the most labor-intensive aspects of our operation sets this project apart as one of the most meaningful products I have delivered so far in my software engineering journey. To this day Supply UI remains a source of insight into how a well-designed system and the use of technology can lead to a tangible improvement of real-world processes.
Credit Card and Payment Management | 8 min
An overview of my journey implementing multiple payment processing methods for Duffl.
Designing a Custom Logger Tool | 5 min
Considerations and functionalities of the custom internal logging tool for HealthSafe ID. Featuring some nuances of TypeScript enums and JavaScript symbols.