Slack App Usage Tracking
Although we have written before about the process of building the DNSimple Slack app, there is one topic that we didn't cover in that post: what we did to learn about how our customers use the app.
Here is what we wanted to track:
- How many customers signed up.
- How many of the sign-ups required a subscription upgrade.
- How many domains were registered through the app.
- Which commands were the most used.
We also knew that we didn't want to put a lot of time (or money) into developing the data tracking since we had already invested quite a bit in the project. Finally, we didn't want to tie ourselves to any specific tool, since there was so much uncertainty surrounding the project.
Baking flexibility in
After thinking a little bit about it, we decided that the easiest thing to do was to use events: We could listen to them and react in any way we wanted. Should the usage tracking become irrelevant at any point in time, we could simply remove the corresponding listeners and leave the application untouched.
The next decision to take was how to implement the event system. Our first impulse was to look at gems that we had used before. However we prefer to avoid pulling in dependencies to a project unless they are really justified.
We then looked into what is availabile in the Ruby Standard Library. We realized that we could build a very simple event bus using Ruby's Observable library. Here is what we came up with:
module DnsimpleSlack
module Events
def self.publish(event_name, event_payload = {})
event_bus.publish(event_name, event_payload)
end
def self.subscribe(event_name, &callback)
event_bus.subscribe(event_name, &callback)
end
def self.event_bus
@event_bus ||= SimpleBus.new
end
class SimpleBus
include Observable
def publish(event_name, event_payload)
changed(true)
self.notify_observers(event_name, event_payload)
changed(false)
end
def subscribe(event_name, &callback)
subscriber = Subscriber.new(event_name, callback)
self.add_observer(subscriber)
end
class Subscriber
def initialize(event_name, callback)
@event_name = event_name.to_sym
@callback = callback
end
def update(event_name, event_payload)
@callback.call(event_payload) if subscribed_to?(event_name)
end
private
def subscribed_to?(event_name)
@event_name == event_name.to_sym
end
end
end
end
end
With this code in place we could report events from anywhere in the app like this:
Events.publish(:command_received, {
type: command.name,
text: command.text,
user_name: command.user_name,
team_slack_name: command.team.slack_team_name,
team_dnsimple_account: command.team.dnsimple_account_email,
})
The simplest tool that could possibly work
With the event system in place we only needed to add subscribers to the events.
Next we had to decide how we were going to visualize this information. Since we didn't know what the volume of data was going to be, and given it was going to be very easy for us to replace the code that managed the events, we did the simplest thing we could come up with: logging the data.
Here is how one of our event listeners looks like:
DnsimpleSlack::Events.subscribe(:command_received) do |payload|
DnsimpleSlack.logger.info("@EVENT> COMMAND_RECEIVED: #{JSON.dump(payload)}")
end
Then we made sure to have some built in searches in our log management tool to make this information easily available.
One year after the release of the app, we are still using this solution today. So far we can get all the information we need from it. We couldn't be happier we kept it as simple as this.
Javier Acero
Programmer. Minimalist. Apple fanboy. Currently having fun at DNSimple. I love coffee and my cat.
We think domain management should be easy.
That's why we continue building DNSimple.
4.3 out of 5 stars.
Based on Trustpilot.com and G2.com reviews.