How Bitbucket enriches their Slack app UX with user mapping

Best practices for @mention translation, authorized unfurls, and interactive messages using OAuth 2

As an application developer integrating your service with Slack, you’ll have to sooner or later deal with mapping users. That is, you will have to figure out how to allow a user to link their Slack account to an account in your service. It’s one thing to simply turn events from your application into Slack notifications. Making the extra effort to map user accounts unlocks powerful features like direct messaging, @mentions, authorized unfurls, interactive messages, and slash commands that allow users to actually drive your application from Slack.

We recently overhauled Bitbucket Cloud’s Slack integration. The first version (released several years ago) simply translated events from Bitbucket Cloud into Slack messages and piped them into your channel. Our shiny new Bitbucket Cloud for Slack app can do much more, chiefly due to the effort we put into implementing user mapping.

In this post we’ll go through some of our new features that leverage user mapping, the nuts and bolts of how we built it, and how we encourage users to map their accounts.

@mentions, shmentions

People are pretty important in both Bitbucket Cloud and Slack. Every notification we send contains at least one reference to a person, be it the author of a commit, a reviewer on a pull request, the assignee of an issue, or the developer who broke the build. Before we supported user mapping, people were represented by their Bitbucket Cloud username:

This is OK, but user mapping makes it better! If the user has linked their Bitbucket Cloud and Slack accounts, we can render their Slack display name instead:

What’s the difference? The second notification refers to Vlas via a Slack @mention, which means the user will be actively notified — by push notification on mobile, or growl on desktop — that an important message requires their attention.

Neat, eh?

But before you go and start translating every username you have to an @mention, be warned! It turns out that users don’t actually like being spammed with @mentions every time something that relates to them happens in Bitbucket Cloud (thanks beta testers!). To fix this, we came up with a way to refer to a Slack user without actually @mentioning them. We call them shmentions, as in Shhh!-mentions, and use them whenever we want to refer to a user without notifying them.

For example, we use @mentions for build failure notifications as the developer probably wants to know ASAP that they’ve broken something. But we shmention for commits pushed notifications, since the user literally typed git push in their shell a split second before and doesn’t need to a Slack message to tell them about it.

Shmentions are implemented by sending slightly different markup as part of our Slack messages:

const mentionTemplate = `<@${slackId}>`
const shmentionTemplate =
`<https://${subdomain}.slack.com/messages/@${slackId}|@${username}>`

Slack turns the former into a proper @mention, and the latter into a link that looks like an @mention, but actually takes you to the Direct Message screen for that particular user. This allows you to quickly query a relevant user about the pull request, pipeline, commit, or other Bitbucket entity that triggered a particular Slack notification.

Disclaimer: shmentions are a bit of a hack, but Slack currently handles them quite gracefully. If you click a message in the desktop app, by a happy coincidence it opens the corresponding Direct Message screen within the app rather than opening the user’s browser. Strictly speaking this is an implementation detail, but we felt that the benefit to UX is worth the risk of changes in the URL structure of the Slack web app (or the behavior of the desktop app) at some point in the future.

Honorable mentions

So when should you shmention, and when should you @mention? Here are a few suggestions based on our experience developing Bitbucket Cloud for Slack.

You should consider a shmention when:

  • The user recently performed the event; or
  • there is no clear action that the user needs to take.

You should consider an @mention when:

  • It’s urgent and/or important that the user sees a particular message (e.g. a developer broke a build); or
  • the user is required to perform a particular action (e.g. they are an outstanding reviewer on a pull request).

In our app, we use a combination of @mentions and shmentions to speed up the pull request process, by exerting a little social pressure on outstanding reviewers.

When a pull request is created, the author is shmentioned (they’re well aware that the pull request exists as they literally just created it) and all the reviewers are @mentioned as their approval is being sought.

However, when the pull request is subsequently approved or updated only the outstanding reviewers are @mentioned. Reviewers who have already approved get a nice ✅against their name and are silently shmentioned instead of @mentioned.

👉 nudging with DMs

User mapping also provides another remedy for reminding reluctant reviewers: direct messages. Pull request notifications display a friendly ‘Nudge reviewers’ button that sends a polite-but-firm message to all outstanding reviewers, asking them to complete the review.

To be able to DM a user, you need to know their Slack account ID, so you’ll need to implement user mapping if you’d like to support ‘Nudging’ in your own Slack app.

Buttons & Slash commands

User mapping lays the groundwork for a two-way integration between your application and Slack. So far this article has discussed translating Bitbucket Cloud users into Slack users. But a map works in two directions! Given a particular Slack account we can look up their corresponding Bitbucket Cloud account. This lets us implement buttons and slash commands that, when invoked, run as that user against the Bitbucket Cloud REST API. For example, Bitbucket Cloud for Slack supports commenting (and replying to comments) on pull requests, issues, and commits, straight from Slack:

Authenticated unfurls

User mapping also allows us to unfurl content on behalf of a Slack user based on the permissions granted to their Bitbucket Cloud account. One common use case we identified for our app is a developer wanting to share particular lines of code with their colleagues, either to show off their work or to identify the location of a defect. Slack’s unfurling feature makes it a simple to render a code fragment within the channel whenever a link to source file is pasted:

Bitbucket Cloud for Slack supports unfurling files, individual lines of code, or ranges of lines.

By making a request to the Bitbucket Cloud REST API on behalf of the user who pasted the link, we can include any repository that the user has access to—not just those that the Slack channel has been subscribed to.

In order to support this, we need to not only link the user accounts, but we also need to securely get the user to grant us permission to access the Bitbucket REST API on their behalf.

A simple matter of OAuth

The gold standard for linking accounts and authenticating APIs is OAuth — specifically OAuth 2. OAuth is superior to other common auth strategies for a few reasons:

  1. integrations are limited to specific permissions granted by the user
  2. the user doesn’t need to divulge their username & password to a 3rd party
  3. the user doesn’t need to muck around generating & revoking API tokens

Instead, with OAuth, the user simply logs into your web application and clicks a button authorizing the integration to access the application on their behalf.

This means the authorization flow for Bitbucket Cloud for Slack is dead simple—two clicks!

If you’re considering implementing OAuth 2 in your Slack app, you should also consider adding some user management slash commands to handle users who might have multiple accounts. Bitbucket Cloud for Slack supports /bitbucket login, /bitbucket logout, and /bitbucket whoami to allow a user to easily switch link and unlink accounts as needed.

If the service you’re integrating with Slack doesn’t yet support OAuth 2, consider implementing it before proceeding with user mapping — particularly if you have an externally facing API. Your ecosystem will thank you!

User, map thyself!

Once you’ve built a user mapping flow, the next step is to encourage users to map their accounts. Don’t message all users with a request to link their accounts upon installation. There’s a specific requirement in Slack’s app directory checklist regarding this kind of spam.

This is particularly important for Bitbucket Cloud, as there are a likely a number of users in any given workspace who don’t write software — Who’s a git? Why would I push them? — and asking them to link their account is only going to get confusing.

Instead, Bitbucket Cloud for Slack takes a less spammy approach to encouraging user mapping. When a user subscribes a channel to a repository, we send an ephemeral message to members of the Slack channel who haven’t yet linked their accounts. This is nice for two reasons. Firstly, the members of a channel with a repository subscription are more likely to have Bitbucket Cloud accounts than the average Slack team member. Secondly, ephemeral messages are only delivered if the recipient is currently active in Slack, so the mapping prompt won’t bug anyone that isn’t already engaged in the channel.

Additionally, we prompt users to link their account whenever they interact with a message button or invoke a Slash command that requires a linked Bitbucket Cloud account:

As another subtle hint, we show a list of unmapped reviewers when someone presses the Nudge reviewers button. This lets the nudger know that some of their nudgees couldn’t be reached, and exerts a gentle social pressure on developers to map their accounts.

I hope you’ve found this post useful for your own Slack apps. If you have any questions or feedback, please leave a comment below or hit me up on Twitter—I’m @kannonboy. If you’re keen to test out what we’ve built, you can install Bitbucket Cloud for Slack, or sign up for a free Bitbucket account!