As I’ve been revamping Split It, I have learned so much about user experience and design. Today, I am kicking off a recurring post series about what I’ve learned in product, design, and the overlap between product and engineering.
Today’s topic is how the user is always right.
I got a text from a friend the other day about how there was an issue with sharing a link to Split It. The problem was that in the Split It Beta, you would have to click the “Copy Link to Invite Others”. You could not click the native share button on mobile, nor could you copy and paste the URL to send to your fellow splitters.
The decision to build it this way was technically motivated, and it saved me some time during the initial development. However, it was the biggest cause of confusion for the app for many years. As a naive engineer, I built it thinking “just do it this way, it’s simple enough to use, just use it how I built it.”
No, younger Cory. Simple enough is an app that works so seamlessly that people are wowed by the utility and flabbergasted that it “just works.” The truly magical experiences are so intuitive that the interface just gets out of your way.
My attitude of arrogance was not only wrong, but unhelpful. I was receiving feedback on my product, and instead of listening to it, I tried to justify that I was right. Today, I try to take a more constructive approach to feedback1.
What I realized is that people don’t like to read copy on a website, and no matter how many words you put on a screen, people are in a rush and try to act intuitively. It’s your job as the designer not to create new ways of interacting with your app, but to leverage the conventions of existing popular apps.
If you’re a TikTok user, you know that holding the screen will play the video at 2x. You may have noticed that other apps followed suit, and now that is the default interaction across videos. If you have a video player, and you don't have that interaction, people will try to do it, and get frustrated that you broke the norms.
Even as I was revamping the app, I was reluctant to build this since it was a hard technical problem, but as the feedback kept coming up, and I realized I was breaking the norms of how people shared links, I needed to find a way to make it work. I figured out a way using some fun authentication logic and query params. It was a great combination of finding a creative technical solution to a user/product problem2.
Now, whether you click the native share button, copy the URL, or click the “Share Split” button, you will be able to invite your friends to split the bill (though I still prefer the button, since it launches a QR code for immediate sharing as seen below).
So next time your users tell you there’s an issue, don’t think of it as a bug. Think of it as an opportunity to make your app work better. I know, it’s hard, especially when you’ve put so much effort into a project. But the fact that people care enough to give you feedback in the first place should be encouraging.
Until next week,
Cory
I hope you’ve found this first entry in the series helpful. In the spirit of this article, if you have any feedback on topics you’d like me to write about, please reach out!
And I am always looking for feedback on Split It.
If you’re curious how it works technically, here we go. There are two ways to authenticate a check in Split It. One is using a bearer token for all requests. The second way is to have an auth_code.
To get access to a check, you need to use a separate code called share_code. There is a /join?share_code=ABC123 route that allows you to exchange a share_code and a nickname for an auth_code. Each auth_code is associated with a specific participant on a check. After joining, it would redirect you to the /checks?auth_code=AAA with an auth_code in the query params. You can imagine that if I copy my URL with my auth_code query param and share it, the next person that clicks it will be authenticated as me.
In order to allow a user to copy the URL and share it, I needed to store the auth_code in the browser, as well as understand if a user already has access to a check. In addition, I needed to have the /checks/:id page redirect to /join if the user didn’t have access. But to do this, I needed to always attach the share_code query param to the /checks/:id link.
Beforehand, when a user would share a link like /checks/1234, the result would be unauthenticated and redirect to the home page with an error. But now, with /checks/1234?share_code=ABCD, it redirects to /join?share_code=ABCD, and knows how to join. No matter how you share a link, it allows another user to join.
While this seems simple in hindsight, I was bogged down in what had been built and a happy path that worked that it didn’t seem worth building. But as I realized that the user is always right, I knew I had to find a way to make this link sharing work from anywhere in the app. It was a massive usability improvement that hinged on finding a way to make it work technically. A constant balance, but a worthwhile one.
If this footnote means nothing to you, you should check out my article on The Power of a Link.