How We Built an Extensible Tour System for Streak

Streak is a process management tool that helps you manage a wide variety of processes and workflows such as Sales, Hiring, Bug Fixing, etc. This flexibility is both a blessing and a curse since it can do almost anything, but people aren’t sure where to start. To help alleviate this issue and ease people into using Streak we’ve launched a new in-app tour system.

When designing our tour it became apparent that getting it “right” would be near impossible without serious trial and error. Traditional approaches for tours involve coding javascript with explicit calls or embedding special tags in your HTML — this makes trying different variations of a tour overly arduous. We needed to come up with something that would let us create different tour variations quickly and easily and even let us do A/B tests with vastly different tours.

The Tour Book

The key breakthrough was to move away from designing the tour through HTML tags or straight javascript code and instead encoding the tour in a JSON configuration file. At a high level we like to think of the tour system as an interactive book, users can flip through pages, going through chapters, sometimes going back or even skipping ahead. In this analogy, each page of our tour book refers to a single tip or piece of help that we are pointing out to a user. Here’s an example of a page in our tour:

The above is an example of a page. The chapter in this case would be the set of Pages that explains the various parts of this entire view.

By writing the tour as a JSON file it makes it trivial to change up not only wording, but the entire flow of a tour through our app.

Here’s some key terminology:

  • The JSON file is called a Tour Book
  • A Tour Book is composed of Chapters
  • A Chapter is a set of Pages and optional Triggers
  • A Trigger is a boolean expression that determines when a chapter is run
  • A Page is a set of Items
  • An Item is either a Widget or Function

Widgets: we have a standard set of widgets that are the kind of different visual elements you’ve seen before. Modals, tooltips, highlight, and overlays. In the Tour Book you can specify the title and contents of the widget alongside what buttons show up and what they do.

Functions: used to execute custom functionality as required by the tour designer. i.e. when a user first signs up for Streak we present them with a page to select what pipeline template they’d like to start with, this isn’t a standard widget but instead a custom function that is called.

How a Tour Is Run

We have our Tour Book, and we have our widgets, but how does the tour actually get executed and started?

Here’s what’s happening in the diagram:

  • When the user makes their first request to the server we assign a random Tour Book to that user in this case it’s MyTourBook.json
  • The Tour Book gets downloaded and passed to the tourRunner class
  • The tourRunner class parses the Tour Book and searches for any chapter triggers and also gets the associated Tour Functions file for this Tour Book which in this case is MyTourFunctions
  • When a trigger in the Tour Book evaluates to true, the tourRunner instantiates the Tour Functions class passing in the Tour Book to the constructor
  • The Tour Functions starts the respective chapter and displays the first page
  • When the user clicks “next” the Tour Functions class automatically loads up the next page defined in the Tour Book
  • This continues until the tour is complete or the user stops the tour early

Technical Nitty Gritties

Outside of the overall architecture there were a few fun technical challenges that we ran into.

JSON Boolean Logic — we wanted the triggers that start a chapter to be totally flexible meaning they had to support essentially any boolean expression. It was an interesting challenge to figure out a way to express an arbitrary boolean expression in JSON alongside a simple evaluator. We ended up going with a prefix pattern which is essentially a readily-made parse tree.

Which then gets evaluated by the following recursive algorithm:

There’s definitely more elegant ways to express the boolean parameters in JSON so would be interested to see what people have implemented themselves.

You may have noticed that there’s a couple of raw strings in the boolean expressions in the JSON above, those are actually function names. This takes us to the next little nitty gritty…

Function References — as part of the Tour Book definition it specifies a Tour Functions class, this Tour Functions class is the context in which the Tour Book is actually executed — in the above example we defined a class called MyTourFunctions. What this means is that the Tour Book can reference functions by name that are defined in the MyTourFunctions javascript class.

Function Wrappers — One of the other cool things about the Function References is that all the functions are automatically wrapped with extra event logging code. As a tour designer when you implement a function in your Tour Functions file you don’t need to add any basic event tracking.

Moving Forward

The tour system has been in development for a little while but we’re just getting started. We’ll be constantly iterating on our first run experience to make it better for new users and are excited to share the learnings we get with fast experimentation.

The other great thing is that the infrastructure lets us do a couple of key things moving forward:

  1. Better in-app help — instead of static documentation that gets quickly out of date we can use the in-app tour system to guide you through explicit steps to accomplish certain tasks. We can use the same infrastructure of tours to let users selectively view certain chapters that they need help with.
  2. Deeper/longer term feature tours — in addition to the first-run experience we can trickle out exposure and tours to users as they get more familiar with the system. We can have triggers that show up after days, weeks or months, or after a user has hit certain milestones and think they’re ready to start using the advanced features.