Pragmatic Way To Send Analytics Data

Jan 7 · 5 min read
Sending Analytic Data

In Tokopedia, we design many systems that need to be changed on a big scale. This could be seen in how Tokopedia’s android app communicates with server instances using json and several other formats such as protocol buffer. Particularly in json, we could add several values such as string, integer, an array of objects, etc. Some server instances such as Google Tag Manager require our developer to pass in several values, as depicted in figure 1.

Figure 1. Example of sending analytic data using google tag manager

As you can see in figure 1, the object of mFirebaseAnalytics has logEvent with a bundle parameter. Bundle is equivalent to Map, but it exists in the android ecosystem.

But there are some challenges for us to use mFirebaseAnalytics, one of them is some misformat Bundle that the android app sent being discarded by the libraries and therefore not getting populated by the dashboard. Also, The misformat might only be found after several releases, as value don’t get shown at the dashboard.. So from that problem, we start to handle the problem properly.

Options that we have

The problem above, we could solve it using 3 options below:

  1. If-else code style
  2. Manually created Design Pattern
  3. Automatically Design Pattern

As you can see at figure 2, we can reformat the bundle or map using standard static functions. But complexity and maintainability cost would come high as we need to know which part should be changed when adding new data format.

Figure 2. Example of formatting bundle using if-else code style

Design Pattern which manually created

In figure 3, we can also use factory pattern after the FormatterBundle object is created, we will get the map or bundle. But here we can see the abstraction can separate and decrease the complexity of codes.

Figure 3. Example of formatting bundle using manually created design pattern

But here also we face a new challenge, as our codes grows then we need to manually add the code into it as it will become repetitive.

Design pattern which automatically generated

In previous attempts, we could avoid the repetitive task of adding manually the code by using an annotation processor. This will benefit us as developer especially misformat data got the error at compile time.

Before we begin producing the annotation processor code, we will introduce some terms first:

  1. Annotated class: a class that has been annotated as shown at figure 4.
Figure 4. Example of annotated class

2. Annotation class: class that would be used in annotation, from figure 4, we could see Formatter annotated with NotThreadSafe annotation.

3. Annotated field: fields of class that belongs to a class that been annotated

After we understand the basic term, then we must determine what we want to achieve:

  1. How keys that must be sent to the server instance get checked, for example in figure 3 and figure 2, we understand ITEM_LIST must be sent?
  2. How do we check the values supplied, for example in figure 3 and figure 2, we understand that ITEM_LIST should contain either `transaction` or `search result`?
  3. How do we override the value for null values?

How keys that must be sent to the server instance get checked?

Figure 5. Annotation class called AnalyticEvent

First, We can declare annotation classes which are depicted in figure 5.

In figure 5, we can see that annotation class AnalyticEvent can be used to annotate the class and it accepts 3 parameters. nameAsKey means the generated class could use the field name as key. rulesClass is a place which developer place the rules. In order to check whether annotated class have some required values such as ITEM_LIST, then we required the developer to pass the rules in the 3rd parameter.

Figure 6. Rule class that used to validating key at compile time

In figure 6, we see ProductListClickRules that will check 3 values in root scope eventAction, event and item_list. If the annotated class that uses these rules doesn’t define the key in annotated class such as eventAction, then the generated class wouldn’t compile errors.

Figure 7. example of annotated class that will generated the Bundle

As in figure 7, we pieced them together in an annotated class named ProductListClick. In ProductListClick, we enable nameAsKey so that the variable name would be key. Thus rules will infer the variable then check against the rules. In this context, because item_list is a required key then it will check against other fields.

Figure 8. Workflow of How Annotation Processor validate the keys at compile time
Figure 8.a. Example of error that trigger when validation is failed

Also in figure 8.a, it showed an error message for AddToCart class, which triggers KaptException.

How do we check if the values for certain keys are valid at runtime?

Figure 9. annotation class called CustomChecker

As I’ve mentioned it before, we need to create the annotation for the field. In figure 10, we also need to declare CustomChecker that accepts 3 parameters. The annotated class depicted in figure 11 uses CustomChecker shown in figure 10.

Figure 10. Example of class that validate value in runtime
Figure 11. Annotated field that use CustomChecker annotation class

The entire process also shown at figure 12.

Figure 12. Workflow of validating value of key using annotation processor

How do we override the value for null values?

Figure 13. Annotation class called DefaultValueString

Like before, we need to create the annotation for the field. Here, we declare DefaultValueSting that would temper the value if value supplied is null.

The usage of DefaultValueString in the annotated class is shown in figure 14.

Figure 14. Annotated field that have String type annotate with DefaultValueString

Last but not least

After solving 3 challenges, we could see that in the future we could simplify or add features without having to rely on certain functions or classes that were manually created. This DRY (Don’t Repeat Yourself) Principle helps us to do more things besides this as the library serves as a layer for that. Also, our code is becoming more composable based on annotations that is used in the annotated classes..

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store