At Garena, we are passionate about technology and focus on building great products that serve our customers and impact their lives. Shopee: our e-commerce application is one such example. With Shopee, people can not only buy stuff with a few simple clicks, but they can become entrepreneurs and sell products in a trusted online marketplace, right from the comfort of their homes. The Shopee android app has over 17million downloads and ranked consistently among top 10 shopping apps across South-East Asia and Taiwan.

Building a great android application that can scale with the increasing user base and evolve with constantly changing requirements is no easy task. We rely on a number of in-house and third-party tools to ensure that our application is stable and bug free. Today, we would like to introduce one such tool, that has helped us keep the health and stability of our application in check.

Introducing DevAlert

DevAlert is a lightweight library provides visual alerts to developers and QA when an issue happens during development/testing phase.

In traditional android development, when an issue occurs, we use logs to dump the unexpected state or exception trace. Printing to logcat is not enough at times as:

  • Logs can be overlooked by developers if we are not constantly monitoring.
  • Logs maybe on a remote device which is inaccessible.

Using this library, you can provide visual warning to the developer/ QA when something goes wrong on your test or internal builds so that critical issues can be highlighted as and when they happen (and can be resolved before going live).

You can find out more about this library here and you can explore how to integrate it with your own app.

Use Cases

In this article we will talk about how we have used this library to improve our android application.

1. Background Exceptions

We follow a clean architecture paradigm in our application. Most of our core logic runs on background threads in small tasks or interactors. We enforce the idea that errors or crashes in our interactors should not crash the application. If such a error happens, often users are unaffected. Since each interactor serves a specific purpose, even if one of them crashes, the user is still be able to use other functionality.

During the development phase, often assumptions about the server response, parsing logic or unchecked calls can result in silly exceptions in our interactors. Because these exceptions silently crash, they are often hard to track down in our internal builds. Even though we used crash uploading tools, and printed error logs, due to lack of constant monitoring, these issues would end up in our live builds.

To resolve this we integrated DevAlert in our application. It allowed us to get instantaneous feedback when a background crash happened, right from the moment, the code is run for the first time in the debug build. Since then we were able to catch lot of potential problems which might have otherwise ended up going live.

2. Visual Asserts

We are big fans of defensive coding practices and rely heavily on code checks and assertions. However, we faced some problems when using assertions. Whenever an assertion would fail, our application would crash. This was a painful experience for our developers and QA, who were in the middle of testing a particular use-case. Also since assertions were stripped out from our internal builds, meant that hundreds of internal testers across different regions who would have had a chance to report a potential issue, were unaware of it.

Using DevAlert, we were able to visually report failed assertions without disrupting the testing flow. Selected high risk warnings were visible in internal builds as well. Now internal users were able to notice these critical issues as they happened and were able to report them. Since then we were able to identify and resolve many issues which were rare and hard to reproduce in our local environment, but could be seen by one of our internal users on a particular device or network condition.

3. Health Check

As your application grows, the code base keeps evolving. This also means that many small changes to existing classes are introduced every day. While unit tests ensure that there is no regression, often the performance impact of these changes is not accounted for. Performance is also impacted by the time period for which the user has been using your application. Maybe after using the application for 1 year, the size of your local database has grown so much that each query now performs relatively slow and overall performance is bad.

To ensure that we can track such performance issues, we created several internal rules. These rules are similar to those in Android Strict Mode, but more specific and customised to our application. Using DevAlert we raised warning when these rules were violated so that we could keep a tight check on the performance and stability of our application.

Let us look at a specific example: We created a rule to ensure all our interactors could run within a specified threshold. If an interactor is running overtime, this means that there maybe a potential problem. (Are DB queries too slow? Is there a lock preventing access? Can the logic be optimised?). After applying this rule, we were able to optimise a lot of our interactor performance and whenever a code change slowed down one of our interactors, the developer would be instantly notified.

We added many similar rules in different layers of our application and this has helped us keeping our application’s health in check.

Conclusion

Once your application has gained traction, the next stage of survival is to ensure that your users are always happy with the performance and stability of your app. This means that no matter where they are, what device they maybe using or even the speed of their network, they should have an amazing experience when using your application.

We hope that you find this library and the tips from this article useful. We also hope to contribute more open source projects to the community in the future. Any feedback is appreciated.