Case study: Tayble - an order processing app for restaurants

How we improved the customer experience at 500 venues. 1 year, 8 people, 500 venues, 500,000 orders processed.

Swift
Kotlin
JavaScript
Go
Beego
ReactJS
Java
MongoDB
Gorilla

We don’t want to wait around when we just need to eat, and we don’t necessarily need a waiter to take our orders. What we do need are our smartphones and an intelligently designed, automated app.

Enter Tayble.

What We Did and Why?

To radically change the process of ordering in a restaurant and limit the role of waiters, a group of bright-minded entrepreneurs came up with the idea for an app which would allow users to choose a meal, make an order, and pay for it. All from their smartphones.

The initial solution wasn’t perfect and needed many improvements before it was scalable. The main problem was that the app’s architecture just wasn’t suitable for expansion, and the team working on it wasn’t ready to enhance the system at that time, so MadAppGang got down to business.

Over the course of just one year, we rebuilt a prototype of the iOS app, made an Android version, changed the backend solution, created a modular POS system, integrated machine learning and OCR for menu mapping, and transformed the payment process. Then we wiped our brows and went out for a beer!

The Story Begins

When we’re eating out, it seems like we’re always waiting for something: for a menu, for the waiter, to organise the order, for the receipt, and to pay. Too much of it, right? With the urban pace of life, which so many of us lead, waiting like this can hardly be tolerated. As much as we need food, the food industry needs automation.

The success of self-order kiosks (pioneered by big brands such as McDonald's and Burger King) and innovations in delivery options (UberEats, Deliveroo, Foodora) only favour this view. The dine-in experience still remains relevant but very few people had taken up the challenge of overhauling the existing systems.

In 2017, Australian entrepreneurs Phil Tran, Ben Burns and Chris McCarney decided to take the self-order kiosk idea a step further, wondering if it was possible to perform every action needed to order and pay for an in-restaurant meal from a smartphone. They were inspired by the big idea of making an app which would cover all the restaurants in Australia. But after an ambitious start – a prototype for one venue – they got stuck.

That prototype was an iOS-only app created for Cafe80, located in the heart of Sydney. It allowed customers to order a meal via the app and pay for it directly from their phones. The orders were then sent to the POS system, made by the local company Kounta, which was installed in Cafe80. The payments were then processed by Stripe.

This one-venue prototype solution was enough to attract investments. But what worked for one restaurant couldn't possibly scale further. Here’s why.

The Existing App Wasn't Scalable

Apart from the fact that the app was designed for only one restaurant in the first place, there were further fundamental issues to solve:

  • No version for Android users
  • Weak backend (not valid for large amounts of data)
  • No tools for integrating numerous POS systems or the inclusion of venues without POS systems installed
  • No backend for payments

Since the intention was to build an app which could make any order from any local venue, and then allow users to pay with just one tap, the architecture of the prototype wasn’t enough. After collecting investments, the business was financially able to expand, but the existing solution served limited goals and wasn’t scalable.

Enter MadAppGang

We were approached and asked to rebuild the app in such a way that it worked in all Australian restaurants, including the small ones without a POS system. Our brief also specified that the app should work smoothly on any platform, that the payments should be properly secured, and the operational costs should be minimal. We got started with Sydney.

In delivering such an application, we embraced and overcame numerous developmental challenges. Let’s take a closer look at them.

1. Replacing the Database

The initial app didn’t require much data – it’s only one venue after all. But when adding new places, tables, menus, and options, it becomes much more complicated. There are around 16,000 places to eat out in Sydney and more than 85,000 in Australia. You can easily imagine how much data is needed to gather and organize all the info for all these venues and include it all in one service.

In the prototype, the backend was created with Firebase – a real-time, document-oriented database backed by Google which stores everything as a single object. This wasn’t the scalable solution we needed. What we needed with Tayble was complex data and functions: geospatial query, to find nearby venues; table plans so users could reserve the table they wanted; nested indexes to calculate opening times; and distributed aggregation to compose accurate reports and invoices.

Firebase wasn’t meant for the functions we needed to fulfill and it also worked unstably in Australia because the server it connects to is located somewhere in the US. We corrected this and shifted the main server to the Sydney AWS area which led to all requests working 10 to 20 times faster.

Our backend solution was to replace Firebase with MongoDB. Since the latter is also document-oriented, we were able to switch to it easily. Plus, with a migration gateway that acts like Firebase but stores data in MongoDB, we could move the existing clients with no service interruption.

With the benefit of scalability that MongoDB offers, we gained:

  • Cluster for load balancing and vertical scaling on demand
  • Built-in horizontal scaling and geographic distribution
  • Docker support for integration into k8s infrastructure
  • Ad hoc queries, indexing, and real-time aggregation

As a result, we added 35 tables and 150 indexes that included information about 500+ venues, brands, menus, categories, and users.

2. Integrating Different POS Systems

It was crucial that Tayble could send orders directly to different POS systems to avoid manual entries. While the prototype dealt with only one POS system, there are more than a hundred of POS solutions installed in Australian restaurants, and each one has its own data format. Some of them have API for integration, others don’t. We needed to integrate every type of venue and every POS system.

In the existing prototype, there was one POS (Kounta) integrated inside the app. It meant that all requests were sent from the app, but the system couldn’t possibly send information back. Every change in Kounta API would require a change in the app, as well as the integration of other API. Moreover, this needed to be done separately for iOS and Android.

We needed a system which could translate orders made in Tayble and handle webhooks and callbacks to follow the status of orders in real time. Focusing on the top five POS systems functioning across the country, we created a modular microservice POS with a unified API so that it wouldn’t matter to the app which type of POS it was dealing with – this eliminated many possible changes.

3. Creating a Custom POS System

At Sydney airport, you can see two extremes: next to an automated McDonald’s with a robotic order-delivery system, there’s a tiny restaurant which still uses paper and pen to write down orders. There are lots of small venues in Australia that don’t utilise any POS system as their order volume is too small. Since we wanted to include as many places as possible, we created a simple POS system for such venues to handle Tayble orders.

With the POS we designed, small restaurant owners got:

  • Real-time support (orders could be seen in real time)
  • Integration with a printer (multiple printer support for bars and kitchens)
  • Alarms for delayed orders
  • A system which collects sales reports
  • An option to put the venue on pause

First, we used Golang to create a real-time messaging hub to keep all active orders. The hub allows the handling of 100,000 orders per minute and can be scaled horizontally if the limit is exceeded. A POS system and the app connect to the hub, keeping WebSocket connection awaiting order status change. The user then gets every order status update via their Tayble app.

Then, we integrated EPSON POS printers with Bluetooth and USB connections. Unfortunately, there was no stable printer driver for Android so we had to create our own.

After that, we realised that it would be more efficient and practical to create a device with our own firmware. So, the next stage was to build a custom POS system. It reduced operational costs greatly and the final price for this all-in-one device was two to three times cheaper than a traditional system with a tablet and an external thermal printer.

Tayble was responsible for installing these devices into venues and paying for their production. We installed 115 custom POS systems, adding places that couldn’t be included in the system in any other way.

4. Creating an Android App

The prototype was designed for iOS only. We released the completed version for Android. Since there were no documented requirements – for example, no user stories or wireframes – our Android developers had to work closely with the iOS team.

In the end, we delivered smoothly functioning versions of both iOS and Android Tayble apps. Owing to Kotlin and the native development used from day one, the app provided user experience on a competitive level. Tayble worked very well on a variety of devices produced by different manufacturers.

As a result, we got fantastic feedback from the users.

5. Polishing the iOS App

The prototype lacked crucial features such as geolocation services, the ability to order ahead, search functions, and user history. Plus, payment integration was far from perfect. We needed to rebuild the app completely, and our goal was to deliver a new UI and an enhanced UX.

The results speak for themselves:

Using Swift 4, we managed to release an early version just in six weeks. We continued upgrading it every two weeks by adding new functions.

Main Screen
Menu
Checkout

The reaction of our end users was tremendous. We received many five-star reviews and maintained a trending position on the local app stores, beating apps by major companies such as McDonald's, EatNow, and Starbucks.

6. Crafting an Admin Management Panel

The Tayble team used to manually enter menus into the database. Now scaled and with more venues included, this was no longer an option. What we needed was a handy and easy-to-use platform capable of managing all the items across various menus, as well as any possible changes.

That’s why we created the Admin panel with the following functions:

  • Ability to add a new venue
  • Management of both menus and items
  • The creation of marketing campaigns (discounts, events, promotions)
  • Reporting (daily reports, revenue reports, invoices)

This panel has several levels of access rights. With such a panel, Tayble moderators fill in all the data while managers of particular venues can modify relevant information by adding new items or deleting ones that are no longer available.

7. Menu Mapping

The app had to include all the menus from all the featured venues. Items could be entered manually – UberEats has 500+ people in Manila doing just that. But to us, it seemed more efficient to make this process automated. Especially when it comes to flexible menus and seasonal changes. A non-automated process takes longer, suffers from human error, and needs a second stage of testing.

We solved the problem two-fold. We provided easy menu mapping on the Admin panel (suggestions, copying groups of items, and templates). Venue managers could log in and manage all the data. This system allowed a new place to get on board within two days. We wanted it to take 30 minutes, so we started discovering new possibilities opened up by machine learning and OCR.

Categories
Menu Items
Prices and Modifiers

Finally, we came up with a menu recognition system. It uses a custom neural network to understand the menu and save it to the database. This way, mapping a menu takes around five minutes. The system automatically recognises text, identifies categories, prices, and items, it also predicts modifications, for example extras and vegan options.

The level of automation is insanely high, and the system is self-learning. The more menus added, the fewer errors there will be. This was a perfectly scalable solution that was able to cover any level of expansion. Exactly what we needed with Tayble.

8. Handling Payments

There were some major problems with how payments were processed in the prototype. There was no backend for securing the process, and the existing system was vulnerable to reverse engineering hacks.

With the backend provided by us, the process is performed in two parts: firstly, the transaction is authorized on the device and then the payment token is authorized by the backend. The card number isn’t sent to the backend which eliminates the risk of “man in the middle” attacks.

There also was no solution for rejected orders. Money was charged from the user’s card the moment they made an order, not when it was approved by a venue, and Stripe charged double commission in cases of rejection which was then paid by Tayble. We didn’t want this to happen any longer.

We changed the system so that money was pre-authorised after making an order. If a venue accepted an order, then the backend was responsible for charging the pre-authorised amount. If there was a mistake or a product was not available, the money was released and sent back to the user. With this system, there were no fees in cases involving an error. In the worst case scenario (no answer from a venue, for example), this amount would be blocked for three days and then automatically released by Stripe.

We also introduced card scanning so users wouldn't need to enter the card number manually. Plus, we implemented synchronization across multiple devices. The card attached to one device can be available via other devices belonging to the same user. All card information is securely stored by Stripe and not by the Tayble backend itself.

The Result

The main idea behind the project was the full automation of every possible action. For users this meant the ability to make and pay for orders in just a few taps. For restaurateurs it meant being able to load, change, and adjust their menus easily and track orders in real time.

This strategy allowed Tayble to outperform huge companies and set promising new standards in the industry. For MadAppGang, it was an amazing challenge to create a competitive app solution with the highest level of automation and the ability to quickly expand and scale.

We appreciate the feedback given by thousands of Sydney-siders. Thanks to their reactions to our project, we were able to identify the most significant parts of the system we needed to improve. This was a complex project which allowed us to overcome challenging issues and propose new, up-to-the-minute solutions the industry desperately needs.

Their flexibility is one of their biggest strengths. They’re definitely top tier as far as coding skills because the company’s very strict about hiring developers who are familiar with cutting-edge languages, but their ability to adapt to changing circumstances and come up with creative solutions really sets them apart.

Technologies

swift
Swift
kotlin
Kotlin
javascript
JavaScript
golang
Go
beego
Beego
react js
ReactJS
java
Java
mongodb
MongoDB
gorilla
Gorilla
Infrastructure: Docker compose, Docker, AWS Lambda, EC2, Route53
Cache: Redis
iOS: Swift4, RealmDB
Android: Kotlin, RealmDB
Payment processing: Stripe payment and Stripe connect, Apple Pay, Android Pay, Crypto payments with Bitcoin and Ethereum
User support system: Intercom
SMS notifications: Twillio
Database: MongoDB cluster
Email service: MailGun
Analytics: Google analytics
Security: TLS1.3 with certificate pinning
RealTime message broker: custom Golang websocket hub
Menu recognition: Tensorflow, Google OCR, custom Semantic analysis, Recurrent neural network

Mad team

Jack Rudenko
Team lead
Serhii Chaban
Lead Android developer
Dmytro Lisitsyn
Lead iOS developer
Eugene Simonov
Machine Learning engineer
Mark Salabutin
Full-stack ReactJS/Golang developer
Denis Provodnikov
Frontend ReactJS developer
Denis Ivanov
Lead Golang backend developer
Ekaterina Kelembet
QA Engineer