United Analytics

Leonid Olevsky
The Startup
Published in
4 min readDec 5, 2020

--

Develop its fun, but after you finish your development you want to see how users using your product and measure your success, for this purpose we are collecting data that represent behavior of the users and storing them for later analysis. There are a lot of ways to do it, you can do it using Mixpanel, Firebase, Google analytics, your own service or other preferred ways.

I am not going to write about how to do it, I am going to describe a solution of how to align with data in case you have multiple platforms, for example server, mobile application and website, in this case every platform logging in different way and a lot of times with different names, this makes it hard to map funnels and track cross platform behavior of users.

JavaScript is very common now as you can do all of development using React and node.js, so I will present the solution in JS.

First question that is rising is how we are going to share the code? There are multiple ways to do it:

  • Copy the code everywhere (Not a good practice)
  • Upload to NPM (You will need to build and update for every change made)
  • Adding as a submodule (if you are using Git)

Adding submodule

From my practice adding submodule is the best solution, you can easily switch between branches and maintain it. You can create new repository (For the discussion I will name it united-analytics) and link it to your repository where you want to use the analytics.

Before we start you will need to create new repository, for the example we will call it “united-analytics”, next step will be to add it to your repository using this steps:

  1. Going to your repository and running this git command
git submodule add https://github.com/yourUser/united-analytics

2. This will add your analytic library to you repository. New folder of united-analytics that tracking what brach you are looking at and new file .gitmodules that will describe your connected repository. (You will need to commit the empty folder and .gitmodules to your repo). Example of the .gitmodules file:

[submodule "united-analytics"]
path = united-analytics
url = https://github.com/yourUser/united-analytics

3. For pulling changes you will need to do it recursive

git pull --recurse-submodules

United analytics

After you connected the united-analytics to you repository lets add the code to united-analytics (in the united-analytics repo). My suggestion is to use this kind of structure:

src
- analytics
- events
- page
- eventType
- enentName
- interface

Now you are asking yourself, Why you need interface? The answer to it is that we are solving the alignment of event names and actions, so every platform can use its own analytics solution with existing infrastructure.

Interface can look like that:

interface IAnalyticsProvider {    
logAnalytics(eventType: string, eventName: string, props: any)
}

And we will need one more class that controlling it all, I will call it AnalyticsController :

import IAnalyticsProvider from "./Interface/IAnalyticsProvider";

let instance = null;

export default class AnalyticsController{
_analyticsProvider: IAnalyticsProvider = null
static get instance() {
if (!instance) {
instance = new AnalyticsController();
}
return instance;
}
init(analyticsProvider: IAnalyticsProvider){
this._analyticsProvider = analyticsProvider
}
get analyticsProvider(): IAnalyticsProvider{
return this._analyticsProvider;
}
static log(props){
if(!!props) {
if(!!AnalyticsController.instance.analyticsProvider) {
AnalyticsController.instance.analyticsProvider
.logAnalytics(
props.eventType,
props.event,
props.innerProps)
}else{
throw "analyticsProvider not initialized";
}
} else {
throw "missing mandatory props";
}
}
}

In this way you could use your own logger and just implement the interface to it and initiate AnalyticsController with your own existing analytics class.

... implements IAnalyticsProvider

init in your app start

AnalyticsController.instance.init(HBAnalyticsController)

Now after we have everything setup we can start and build the events. As we are using props it is objects tat is represented in key/value structure, so we can build our report in this way, for example report can look like that:

{
pageName: "my_page",
eventType: "click",
event: "my_button_action"
...
}

In order to achieve it we will build it in aggregation way, its meaning that for every property we will build a function that will return the key and the value of the property, and if we would like to add new property it will be as simple as adding new function in the relevant file that represent the related key.

lest’s look in the example of “pageName” property file, so as I mention in the structure, this file will be located at src\analytics\events\page\page.ts :

function processPage(props, name){
if(props === undefined){
props = {}
}

let innerProps = {}
innerProps.page = name

props.innerProps = {...props.innerProps, ...innerProps}

return props
}

export function myPage(props){
return processPage(props, "my_page")
}

and same for other properties accordingly, for eventType property it will be src\analytics\events\eventType\eventType.ts:

function processEvetType(props, name){
if(props === undefined){
props = {}
}

let innerProps = {}
innerProps.eventType = name

props.innerProps = {...props.innerProps, ...innerProps}

return props
}

export function click(props){
return processEvetType(props, "click")
}

Usage example

After we finished to define all the events we want to log and add the submodule to our project and initiated the lib we can start using the builder in order to log events, it will be simple as it:

AnalyticsController.log(myPage(click(myButtonAction())))

This way we can log over all the platforms, and wrap any logging library that we have already and have correct and aligned data across all the funnels.

This is proposal how to organize and sync between different repositories, and in perfect world they are using same language so it is simple, what if they are using different languages for development?

In this case you can use similar solution, but you will need to add one more step that to automate a build process to generate output files to specific language that you are integrating with, for example:

Hope you enjoyed , please leave comment if you have suggestions or improvement proposals.

--

--