Vogo - Dockless Scooter Rentals
Engineered and implemented the entire Android app single-handedly.
Being an Android Engineer, I went crazy over maps and payment based real-time mobile applications that served millions of customers and wanted to get my hands dirty on them. Vogo, a dockless scooter rental startup based out of Bangalore, wanted to do a major revamp of their entire product and codebase to take their brand to the next level. They had just gotten their funding from Ola, one of the key players in the transportation sector of India. Vogo was looking out for someone who could build a scalable and a visually pleasing Android app for them. Having seen my early works, the CTO of Vogo approached me and asked me if I could help them out. With my interests and their vision coinciding, we couldn’t agree more.
Architect and implement the app end to end single-handedly.
Work closely with the design team to bring out the best possible UX. Suggest changes if needed.
Work with the backend team in finalizing the API contracts and suggest better modelling of the API if needed.
Freelance App Architect - Vogo
April 2018 - August 2018
Re-architect the existing app for improved features, better user experience and scalability
The existing app was built with minimal features and does not scale well enough to serve a huge customer base. The following were the only functionalities offered by the existing app:
A simple home screen that has a list of scooters near the user’s location.
No maps to see your current location and the vehicle’s location.
Research and Insights
Before jumping into the architecture and the implementation, it was essential to get the context on how the existing system works. It was important to empathize with the users of the existing app and understand their experience and expectations. On the other hand, I had to understand why the existing app affects the business and the developers. During the research, I found the following pain points of the existing app from disparate perspectives:
No support for dynamic pricing.
No alternative ways to communicate important information to users during an outage.
No support for offers.
Stagnant customer base + losing customers to competitors.
Inefficient help sections impacting customer care centers.
No maps to locate your scooter intuitively.
Users cannot choose scooter of their preference.
No support for digital payments.
No support for help sections and FAQs.
Incredibly slow app.
Unorganized information on the home screen.
Arduous efforts put in code maintenance.
No separate environment to test the app.
Separate codebases for multiple apps shipped.
Redundant work done in fleet app.
Post research, there was a diverse set of problems that had to be solved efficiently. On analyzing the existing app, the vision of the product, and the enhanced design of the app, I got a clear idea of the requirements. The new app demanded the following characteristics:
Plug and play features
Responsive and smooth UI
Inversion of control
Pre-authorized digital wallets
Flexible modes of payment
Live location and maps
The main flow in the app was to book a scooter, pick it up in one of the stations, use and return it to one of the stations. The source and the destination stations could the be same or different. To book a ride, the user must have signed in and should have his/her driving license verified. Users may pay at the end of the ride or before beginning the next ride. The following diagram illustrates the overall booking flow:
After talking to product managers and the designers for a brief period, the features were finalized. Here, my role was to evaluate the technical feasibility, recommend the best possible solution to the CTO, co-ordinate with the back-end engineers, and then implement the features. Few of the important features were as follows:
View past rides
Sign Up/Sign In
Third party login
Second Factor Auth
Time to destination
Talk/chat with customer support
Terms and Conditions
To use the existing codebase or to create a new one?
Deciding between using the current codebase and creating a new one had its pros and cons. The following were the factors involved in choosing one of the two options available:
Use old codebase
No need to meddle with app signature
Use existing logic in some places
Time to remove/refactor components
Updates to existing app is an uphill task
Create new codebase
Updates to existing app unblocked
Reuse package name
Rewrite existing logic
Given the pros and cons of both the options, I went with “creating a new codebase”, as using the existing codebase would make it difficult to refactor existing logic and make changes to the codebase in such a way that new features are built with ease. Going with the older codebase will take more time than rebuilding the entire app. I explained in detail the pros and cons of both the approaches and Vogo was convinced with going ahead with a new codebase.
Vogo gave me the freedom to decide on the architecture and the implementation. Throughout the process, I was carefully evaluating the trade-offs of different implementations and whether to use an external library or to build it in-house. It made more sense to use external libraries though they come up with some cost, as time was a major constraint. I had to ship the minimum viable app as soon as possible and so I had to make decisions that will save me time. If needed, I could address those quandaries at a later point in time.
I had to think from multiple perspectives in order to implement a reliable and performant app adhering to all the insights as analyzed previously. The following were going through my mind as I was thinking about the overall app in terms of architecture.
Since I was a freelance developer, there were more constraints as I was working full-time with Zeta. I only have the extra hours in the morning and evening in addition to the weekends. Also, I always had a buffer time for Zeta to address production issues if any. The following were my major constraints:
Time: Tight deadline to ship the first version of the new app.
Lack of developers: I was the only developer working on the app.
Collaboration: I had to collaborate with product designers and back-end developers while working remotely.
In order to clearly segregate the responsibilities of different classes in the app, I started with an overall architecture diagram that clearly defines what a specific layer is supposed to do. A given class at any time should belong to only one of the following layer:
UI layer: Responsible for taking inputs from the user and responding to data changes.
ViewModel layer: Responsible for passing data between UI layer and data layer by making necessary mutations.
Data layer: Responsible for business logic, storage, security and caching.
Network layer: Responsible for communicating with the server to send and receive data.
At any given time, a class should fall within one of these layers and a class should not be overlapping on multiple layers.
Adhering strictly to the Separation of Concerns(SoC) principle, independent logics were moved to separate modules. By doing this, we are sure that each module has only one concern to address. Also, in the future when changes had to be made to the system, only the module which is affected needs to be changed. For example, if the underlying implementation of payment logic changes, only the payments module will have to be modified and the rest of the modules can stay as such. By modularizing, we also get to reuse the objects across the app. For example, to spin up a new app for fleet management we could just reuse the modules thereby requiring minimal changes to ship an app with minor changes in the business logic. The modules created and injected through dagger, with each module having a group of classes implementing the business logic but exposing only a public interface as the API.
1. Choose a Vogo station
2. Enter destination
3. Estimated travel time
4. Choose a scooter
5. Enter OTP on scooter
1. User's wallets
2. Vogo credit statement
3. Payment success after a ride
1. Enter mobile number
2. Enter OTP
3. Upload driving licence
4. Choose from gallery/camera
5. Confirm and submit driving licence
The new version was shipped in August 2018. After shipping the revamped app, the growth of active installs was humongous. From 2016 to 2018, the count of active installs remained pretty steady at around 10k. Post August 2018, the number of installs skyrocketed exponentially. As of April 2019, the number of active users are around 275k, achieving a 20x improvement over 10 months.
Rides and Transactions
The growing demand for the last mile travel through scooters increased the revenue of Vogo, boosted majorly by the new app. The following are some of the other metrics(as of April 2019):
15k transactions/day => 450k transactions/month
Fleet size: 8k => Scaling up to 20k
What people say?
People were comfortable using the new app and they started demanding Vogo in multiple parts of the country. As of April 2019, Vogo is fully operational across three major cities in India: Bangalore, Chennai and Hyderabad.
The following are some of the user reviews in the Google Play Store:
App Stability and Performance
Stability and performance are two of the rudimentary characteristics to be exhibited by an app for a fairly good experience. Conscious effort on both these categories yielded a decent result for an app being used at such a huge scale. The following are the metrics from Google Play Store two months after shipping the revamped app:
Size of APK(Production mode - ProGuard Enabled): 4.7MB
Average Gradle Build Time(Clean build on production flavor): 36 seconds
Average Gradle Build(Incremental build on production flavor): 6 seconds
Not all developers get the once in a blue moon opportunity to work single-handedly on a project that has a huge impact on the business and scales exponentially. The journey was adventurous and I was pumped up with confidence after seeing the output of my work on production environment. Here are some of the biggest learnings from my work:
When overwhelmed with multiple tasks on the plate, the best possible thing that one could do is to prioritize. Setting up a list of tasks and assigning a priority based on the changing needs of the organization helped me a lot here. I defined priority as a blend of the impact of the task and the time taken to accomplish that task. The low hanging fruits to be targeted were the tasks that had a huge impact and required minimal time/efforts to change. When presented with a complex problem, break it down to simple tasks and prioritize.
Since I was working full-time for Zeta, my priority for tasks related to Zeta was higher. There could be production issues at Zeta and I cannot leave that as such. To counter this problem, I shared an estimated time to reach different milestones with a comfortable buffer period. If those buffer periods were still available, I would dedicate them to address the issues on the features built till date. In that way, I was able to build the entire app ahead of the timeline shared with Vogo, with supreme performance and stability.
Think in terms of scale and error resilience
One of the thoughts that ran through my mind was that people will be using Vogo app 24x7 across multiple cities. A single blunder will have a huge impact on the business, which is not affordable. When I built something new, I always thought in terms of scale and had back up options if at all something went wrong. For example, the ability of the app to respond to any location in the world is important and it shouldn’t be constrained to only one country(India) or a specific region. All the error responses from the server were routed to a class that takes in a firebase configuration to decide how to react to that error. For example, if the auto-debit authorization of the Paytm wallet had expired and if the server throws an error, the user should automatically be taken to the screen where the Paytm wallet could be re-authorized for auto-debit.
Long term solutions vs Short term solutions
All developers land in this situation at least once in the lifetime. The solution to this quandary is to think in terms of impact. Is it a production issue? Do the immediate fix that unblocks users to use the app normally. Plan and implement a long term solution that avoids similar issues in the future. You can make it a part of the next app version. If you’re a server engineer, you could fix and deploy the changes in the upcoming release cycle or do it at the midnight(or any time when the number of users is very less) depending on the complexity and impact of the change.
Developer experience and configurability
To improve the developer efficiency, it was important to think about a feature in multiple perspectives. There could be new changes to the app, which shouldn’t take much time if it is built on top of the existing code. When isolating independent business logic, I made those logics take in the possible paths as configurations. For example, in the payments module, I exposed a boolean that enables/disables auto-debit from Paytm, which is in turn wired to the configuration module(based on Firebase Remote Config). If Paytm’s auto-debit stops working all of a sudden, a simple toggle from the Firebase dashboard would disable the feature for all/specific sets of users.
Collaboration with multiple teams
Since I was the only developer contributing to the app, in addition to building the app I had to collaborate with multiple teams across the organization for various reasons as mentioned below:
Designers: Get to know about the interactions and edge cases in various features(Eg: Empty state etc).
Backend Engineers: Finalize API contracts and evaluate the feasibility of the implementations. Suggest changes to the contracts if better API design in possible.
Operations/Customer Support: Keep them posted about the changes in the app and prepare them to attend queries from customers concerning the new app.
Management: The timelines and the features built should be updated regularly to the CEO, CTO, and the COO. Additionally, I discussed the features end to end with the CTO before implementing it. This was extremely useful, as I need not have to make many changes post the implementation.
Content that is not discussed here but worth discussing in person:
Usage of Kotlin
Other third party OAuth Integrations for sign in
Detailed discussion on app performance and stability
App interactions and user interface
Built "Card Printer" library, once rated as top trending libraries by GitHub
Contributing to Android, backend, and core infrastructure. Masterminded cost savings of over $250,000 annually.