Guest profile at Shoffr

If you have booked a few rides with Shoffr and come back for the next, you might notice that the “Use Toll” checkbox on the checkout pop-up is already set to your favourite setting. This is enabled by the user profile module we launched last week.

Why?

The “Use Toll” option in our booking flow has had an interesting life. It started out as a way to reduce customer queries about whether or not we will take the road – the guests could choose themselves and allow us to price the trip transparently. We built it like an automation feature but it turned out to be a delight instead – people love the fact that they can choose whether or not they pay the extra toll amount.

The obvious next step was to “remember” which guests like to use the toll and which not and to personalize their checkout experience. Generalizing this thought process led us to the construct of a user profile where we can store what we know or can derive about a guest. The user profile can then serve as a central knowledge bank about our guests and a springboard for all kinds of personalization.

How?

The initial design for the user profile was pretty simple. We would model the profile as an internal sub-domain of the User domain, calculate user profile attributes as part of various user workflows and expose the profile via an API.

However, we soon realized that treating profile as an internal component of user domain would limit its power. User is, after all, a central entity and many other domains can derive information about a user. e.g. The payment system can determine a user’s favourite payment method.

So we decided to apply the The Golden Rule of Platforms to this and split it into two parts. The core profile module would simply allow CRUD over profile attributes – it would not define what the allowed attributes are or understand what they mean. That meant no enums or static definitions of any kind. External module like user or payments or trip would define their own attributes and manage them. The profile attribute thus remains a part of the user domain, but now exposes its capabilities for user and other domains to use.

At implementation level, this was fairly simple. Just two new APIs – getProfile(userId) and saveProfileAttribute(string, string) and a table to store profile attributes. Note how the string parameters in the saveProfileAttribute allow us to put in “whatever”.

With the profile platform in place, we moved to the product engineering bit and wrote the plumbing to populate the toll preference. Every time a customer makes a booking, we trigger an asynchronous flow to see whether the majority of the user’s bookings in the last 60 days use toll or not – and populate the preference accordingly. We also wrote a job to backfill this data for all customers.

Now it was a simple matter to load the user profile on the checkout screen and populate the toll checkbox according to the value stored in it.

Rollout

The rollout happened piecemeal in the order of execution described above. The backend pieces were all completely decoupled so could be released with out any need for a feature gate. The website change was rolled out with a feature because “what if” but the gate was quickly enabled without any problems.

Evolving

A more evolved version of the profile attributes would make external modules “define” the data type and other parameters of the attribute they want to populate and apply some validation using this definition. However, we are holding on to this thought to see how many use-cases truly emerge to require this.

The algorithm for determining the toll preference can also be evolved to be more weighted towards recency or to determine two preferences – one for business travel and another for personal travel. Again, we felt it would be overkill to do this now.

Leave a comment

I'm Veronica

Welcome to Craftfully, my cozy corner of the internet dedicated to all things homemade and delightful. Here, I invite you to join me on a journey of creativity, craftsmanship, and all things handmade with a touch of love. Let's get crafty!

Let's connect