We have been approached by Kieran - CEO from The Burger Collective with the plan to build a mobile app for burger lovers. Kieran has assembled an experienced team ready to execute on their long thought out idea and they needed a technical partner to build iOS and Android app for their members. Restaurant POS app which runs on iPads, Admin section and powerful back-end which can handle growing user base.
We loved the idea and jumped on board!
1. Technology choice
1.1. Mobile App
We knew from day one that The Burger Collective wanted to launch both iOS and Android apps at the same time. We have considered few options - native apps, React Native or Ionic. Early stage startups are always limited by funding so we needed to deliver the best possible experience with minimal costs. Another consideration was rapid development and on the fly deployments which can bypass lengthy app store reviews.
We have considered Ionic for a while. Mostly because of getting the web app as well as Android/iOS mobile app almost for free. But from our experience Ionic apps fall behind native apps in animation speed and in displaying rich content properly. For this reason it wasn't appropriate for a user facing app.
1.2. Restaurant POS App
Restaurants run their separate app which can read a user QR code and associate orders to their accounts. It's an important piece of the puzzle in TBC business as users can redeem their vouchers, get member’s discount and collect points for purchasing and reviewing burgers. Although the purpose of the restaurant app is quite different than consumer facing app and the app is distributed via private enterprise channel, we decided to go with React Native for the same reasons as with the user app.
1.3. Back-end and admin UI
We had to deliver 3 important bits on the back-end:
- API which will handle requests from mobile and POS app - many API calls were geolocation heavy, we needed to make sure API will scale with growing user base. Benchmark was set to support up to 5000 concurrent users.
- Admin UI - TBC team needed to execute various operation tasks without developers, such as approving reviews and new restaurants, removing inappropriate content etc.
- Admin section for restaurants where they can update their details, working hours and menu.
First important step was choosing a database. After initial data modelling we found that data doesn't need to be too heavily relational and we didn't need to support atomic transactions on multiple tables. Also, apart from basic data queries we needed a database which worked really well with geolocation queries. Hence, we decided to use MongoDB as our database. Another reason was that with MongoDB we could use excellent admin UI / CMS keystone.js which saved TBC a lot of time and money compared to custom solution we would have to build. Keystone.js is built on top of express.js which is de-facto standard for api development in Node.js; another problem solved. For a restaurant admin section we decided to build a small custom react.js app as this required more restrictive approach and custom UI elements to make restaurant details updates as easy as possible.
2. Application Hosting, deployment, monitoring
Startups in early stages usually can’t afford on-premise servers and staff to service them. And it would be foolish to even think about this choice when you don’t know how big your initial user load will be and how fast you will grow. So here the choice was quite obvious to select a cloud provider which will cope with flexible startup needs.
We chose AWS as we have the best experience with their services, especially Elastic Beanstalk. It allows us to streamline deployment process and with a little setup, it connects well with CloudWatch where we can monitor application load and get alerts if anything goes wrong. Elastic Beanstalk main advantage comes when you need auto-scaling of application servers. With just 2 boxes to set, you get fully working load balanced fleet of instances which will grow with user load. You need to set up to how many instances you want to spin and most importantly, when you want to do so. EB gives you multiple options, the most common is to set a threshold, when CPU utilization reaches some specific value. We recommend to load test every app with most common usage scenario to find out where this threshold is. This can significantly improve autoscaling efficiency and lower costs.
3. Hot releases with CodePush
We live in times where change is an essential part of our lives. This puts a huge pressure on development teams, mainly to make development cycles shorter and to be more responsive to incessant changes in requirements. We were able to accommodate many new changes in application UX and to introduce hotfixes in matter of hours or days based on our member’s feedback. We also managed to push out new versions immediately. This flexibility wouldn’t be possible without employing a CodePush service which allows to deploy React Native or Cordova applications instantly without going through Apple App store or Google Play store approval process. This approach helped us to connect with our members and quickly react on how they use the app.
Every decision made during design & development process needs to be based on hard data and insight about in-app user behaviour. Otherwise, you are just guessing and this might lead to decreased potential ROI and suboptimal use of time & resources on development. In our case, TBC team decided to employ MixPanel service to fully understand user’s journey through the app. The MixPanel provided TBC team with necessary information to make the right design decisions, test user behaviour with A/B testing, engage with users through segmented push notifications and provide analytical data for our executive & marketing teams.
From developer’s perspective, the MixPanel implementation requires adding event trackers into various places in client's apps, depending on what they want to monitor. The MixPanel React Native wrapper is available on GitHub which makes integration with the app rather easy.
AppsFlyer service is a mobile attribution & marketing platform which helps marketers to understand effectiveness of marketing campaigns. TBC marketers were able to see ROI for different campaigns (a number of new app installations as a result of a specific campaign - e.g. on Facebook) and adjust their decisions from what they learned through AppsFlyer.
As with MixPanel, React Native wrapper component for AppsFlyer is available on GitHub.
5.1. Server side-testing
When it comes to testing the server-side code, we prefer the maturity of Mocha framework together with Chai assertion library. In NodeVision, we use BitBucket as version control system which comes with so called, Pipelines. You can achieve Continuous Delivery with this tool quickly and without cumbersome configuration. Every commit into BitBucket triggers automated tests, which provide immediate feedback about potential issues in your code base.
5.2. Client side-testing
We adopted more traditional user testing with a prepared set of user testing scenarios. Before every release, our testing team carries out a full regression test on reference devices to ensure full functionality of the app. We are fully aware that this situation is not optimal as we can cover only limited number of devices (this is problematic mainly for Android ecosystem). Moving forward, our plan is to switch to more automated testing with AWS Device Farm. This would allow us to cover numerous phones from different manufactures and to automate test with test scripts.
6. Load testing the app
As mentioned above we needed to ensure the app can handle ~5000 concurrent users. We also needed to test that auto-scaling will spin new instances at the right time. We used https://artillery.io/ framework to load test production servers. With a chosen scenario of the most likely app user journey, we let Artillery to add more and more users and we then monitored the output. We managed to get up to 6 t2.large instances, handling all load with very fast 36ms average request latency.
7. Crash Reporting & Logging & Monitoring
7.1. Client Side
There are many products and tools for app monitoring and your choice might be affected by multiple factors such as, cost, supported platform, range of analytics, details about crash events, etc.
In NodeVision, our number one choice is Crashlytics which is a part of a bigger platform - Fabric. It gives you every possible information for any crash which has occurred in your app. You can extend crash reporting with your custom metadata which could help you to duplicate an issue in your dev environment (e.g. user id, screen, user action etc.).
Another great advantage of Crashlytics over the other tools is that it’s free, even for enterprise sized project. A React Native wrapper for Crashlytics is available as an open source project on GitHub which allows to push costs of implementation down.
7.2. Server Side
7.2.1. Code execution and health monitoring - Loggly
For server side logging and request monitoring, we used Loggly services. It helped us to detect any anomalies in our server code execution and to monitor response times. It provides detailed error reporting and allows to monitor key resources and metrics. Basically, Loggly gives you everything your team needs to quickly react if something goes wrong or even before that.
The AWS comes with extensive resource monitoring which could be extended by custom metrics and alarms. This allowed us to keep a close eye on server resources and proactively react during high workload periods. Generally, the AWS tools don’t provide a deeper insight into potential code issues. This requires different tools which are integrated into your code base (e.g. Loggly).
As mentioned before, the AWS metrics are an essential part of auto-scaling management. It allows to optimize a number of server instances based on the current load. We were able to configure AWS EB instance in a way that any excessive workload automatically increased a number of server instances. E.g when our marketing team ran a promo campaign which resulted in increased number of requests.