Company
A status update
Matt Vagni
Co-founder & CTO
Sep 10, 2024
Over the past week we’ve launched some larger changes to Plain’s core statuses, workflow and data model, and I wanted to explain why and how we got here, as well as what’s next.
This will be a bit of a retrospective on some early design and product decisions, why some of those decisions didn’t work out, and how we’ve iterated to where we are today.
Inbox zero as a goal
When we launched Threads in November 2023, we may have gotten a bit too existential as product designers and opted for a very simple set of statuses to categorize every thread in Plain. We broke threads down into one of these three statuses: Todo, Snoozed, and Done. Our thinking at the time was that, irrespective of your workflow, channel, or company-specific challenges, support requests either need you to do something (Todo), are waiting on something (Snoozed), or are Done.
Very much influenced by our own startup founder email mentality and tools like Superhuman, we approached designing the Plain workflow in a similar way. Our thinking was that a good support tool should get you to inbox zero as quickly as possible, with the goal of getting through support requests (“Threads” in Plain) as fast as possible. In practice, given our statuses, this meant that we would take every thread and, once we replied to the customer, we’d mark it as Done, as, technically, there was nothing further for us to do. Done was actually “done for now” as, more often than not, we hadn’t actually resolved the issue the customer was facing. This meant that threads would jump between Todo and Done many times before ultimately settling in Done when the customer’s issue was actually resolved.
This worked – kind of. Inbox zero was achieved many times a day, but something wasn’t quite right. What became clear to us, and early users of Plain, was that this approach was too simplistic and not a good fit for B2B.
The first issue was that it felt odd to mark a thread as Done when it wasn’t actually resolved. Especially in a B2B context, where a single customer represents a potentially high-value contract, you found yourself leaving a thread in Todo just so you could follow up if the customer never replied. Ultimately, even though technically speaking, you had done your part by replying to a customer, it was more important that the issue was actually solved.
The second issue was that it made it weird to capture work happening as part of investigating the issue. Specifically, if you created a linked Linear issue and did some engineering work to resolve a problem, it was odd to see a support request in Todo as a fellow teammate. You’d basically be forced to read the last message or note to understand what was actually happening.
The third issue was that Snoozing’s purpose was unclear. When new teams adopted Plain, they were unsure when to use it. Some teams started snoozing every thread and only using Done when something was actually done. This kind of worked, but when a customer stopped replying, it would result in threads being snoozed and unsnoozed dozens of times in a row before finally being marked as Done. By extension, because the purpose of Snoozing was not clear, it muddied the water as to the purpose of Done.
Another issue was around reporting. By having threads move between Todo and Done dozens of times before settling into Done, metrics around resolution time were deeply flawed. Specifically, it meant that your resolution time would fluctuate within a previously occurring time period, as the threads in that period might not actually stay in Done but could, at any point, come back to Todo.
Adding reasons
Our first attempt to fix some of these issues was to add more detail to each status. We called these “reasons” in the Plain app.
The idea was to show more information on a given status so you could better understand the nuance of why something was in a given state. Within Todo, for example, we’d keep track of and show you whether any given thread was new, had new customer messages, had been replied to already, had Linear updates, or had been unsnoozed.
This allowed you to suddenly see through a long list of threads in Todo and get a better sense of what actually needed action from you (e.g., has new messages or has a Linear update) and what was in Todo because the customer had yet to reply to us.
This also, for the first time, allowed teams using Plain to actually use Done as Done. You could reasonably mark a thread as Done only when you believed you had solved the root issue.
For a long time, this approach held up, allowing many teams to grow and scale on Plain. However, at some point, statuses started to break in a different way—not because of the statuses themselves but because of how they worked in the UI.
Like any diligent engineering team might have done, we had modeled our thread statuses as a state machine. How it worked was that when a thread would change state, we’d look at the event (i.e., the reason) it changed status, and that would then be set on the thread. This meant, practically speaking, that if a thread was in Todo because the customer had sent a new message, the reason would be “new reply,” but you couldn’t change this manually.
This caused problems, even in basic scenarios. Take this conversation, for example:
Customer: I have an issue with X.
Support: Looking into it now!
At this point, the customer is still very much waiting for a reply, but the status here would be wrong, as the last message was outbound.
Although reasons were useful, not giving our users a clear path to change them made them less useful as a way of actually managing your support queue. Users of Plain perceived these more as metadata than an actual status of the thread.
On a purely UI level, they were also not as useful as they could have been, because we only ever showed them as part of threads and filters, but never built out top-level queues to use them.
Lastly, and crucially, they still didn’t fully address the confusion around Snoozing and Todo. By now, leaving most threads in Todo until fully resolved, a lot of teams ended up with unusually large queues in Plain, where most threads were waiting for a customer. Snoozing was still the only real way to make those threads disappear from the main queue in Plain, allowing you to focus on what actually needed your attention.
Buffing reasons
The specific problems we wanted to solve were:
Split out and disambiguate threads that are waiting on you vs. threads that are waiting on the customer.
Make the workflow you should follow more obvious and clear up any confusion around when to use what status.
Make Done actually mean Done.
Make Todo actually be things that are in Todo.
Allow you to capture when a thread in Todo is being worked on actively.
Sketching and trying things out, we realized we had the makings of a really solid system; we just needed to reinforce and build upon reasons.
The approach we took was to elevate reasons to actually be more like sub-statuses, add top-level queues in Plain, and, crucially, let you set the sub-status on a thread.
Today, the hierarchy of statuses (Todo, Snoozed, Done) and their sub-statuses look like this:
To make Todo more powerful, we added a new sub-status called Investigating. This meant that when a customer’s issue was particularly thorny or needed work to happen outside of Plain (e.g., looking into logs, debugging what happened), the thread could be set to Investigating. Besides that, we cleaned up the existing reasons by taking a closer look at the language and terminology we used, ensuring everything aligned well with our SLAs and other features.
To solve the confusion around waiting for the customer and Snoozing, we added two new reasons to Snoozed: Paused for Later (time-based) and Waiting for Customer. This allowed you to clearly mark a thread as waiting on the customer. As part of this, we ensured that Pausing would return the thread to whatever its previous state was. This gave Snoozing a very clear purpose and definition: threads that are waiting on something before they can be picked up again.
Done was basically left unchanged, but its purpose evolved, now genuinely being used for when a thread was truly done.
Making it smooth
To make this all work, it wasn’t enough to just update some links and filters in the UI. We wanted the workflow to feel natural and obvious. So we spent a long time looking at the actual core interactions within Plain to make sure everything was as smooth as possible. Everything from shortcuts, ⌘ + K, timeline entries to notifications.
This also meant redesigning our composer. This was our starting point before any of these changes:
This is where we landed:
We made it much easier to reply and change the status, added a new status picker to manually change the sub-status of any thread, and centralized other actions such as priority and assignee, giving you a central place where you can do everything you need on a thread (besides ⌘ + K, of course).
The bill, please
It was a complicated release to plan out and ship—status changes are always hard. It required us to also take on a bunch of other work, such as improving how our webhooks are versioned, and making numerous other API and documentation changes.
Overall, however, it was worth it, and we think we got it right. Maybe unusually, a lot of our customers who tested this change basically had no feedback besides, “Yes. Exactly.”
For something so fundamental as changing core statuses and workflows, as far as I’m concerned, this is as good as it gets.
What’s next?
We’re not done yet. This first release of statuses shipped the most core changes, but with this new system in place, we can now layer on a lot of other improvements.
In the next few weeks, you can expect the ability to mark a thread as Ignored (this will be a sub-status of Done), and threads will also be able to automatically be marked as Done if they’ve been waiting for the customer for more than X days.
Purely on the UI level, we’ll soon be launching our new queues including a Kanban view, which will tidy everything up, increase space efficiency, and make working with our new statuses even easier. But that, we’ll save for a separate blog post. ;)
Want to check it out yourself? Go to app.plain.com and sign-up.