December 08, 2008

Smart Return in Sifter

We put significant amounts of thought into many of Sifter's features. One of my favorite features to build and use happens to also be one of the most invisible features, and it provides a great example of how design is sometimes about the things you can't see.

Just in case you missed it, Sifter launched last week. With all of the new material, I wanted to get back to sharing some of the behind the scenes details about designing Sifter.

Today, I wanted to focus on a feature that exists almost entirely behind the scenes. It’s more about interaction design than tangible aspects like visual or interface design, but it’s just as relevant to creating a good user experience. Just because a feature isn’t visible, doesn’t mean that it’s not valuable.

The Feature

I have been calling the feature “Smart Return”. The idea is simple. Whenever you’re viewing a list of issues, it’s possible, and likely, that you’ve sorted it a certain way or filtered the page so that you only see the issues that you care about right now.

The problem is that when you leave the page to create an issue or update an issue, we need to make sure return you to the page you originally came from as well as remember the state of the page so you don’t have to reapply your sort and filters.

A Real World Example

For instance, let’s say that I have a dozen issues that are assigned to me. Of those, I decide to focus exclusively on the critical and high priority issues. Since the list is short, I go ahead and sort them by when they were created. That way, I can make sure to address the oldest issues first. So, at this point, my page is filtered and sorted on several different parameters. (Figure 1)

Screenshots showing the issue listing page before and after being filtered and sorted. Figure 1 One of the key ways to interact with issue lists is to filter and sort them into a list off issues that you can work through.

Now, when I click on an issue, I’m taken to the issue details page. After I take some action on this issue, it would be annoying to have to sort and filter my issue list every time. So, regardless of what I do next, I’d ultimately like to return to my issue listing sorted and filtered the way that I left it. (Figure 2)

On the surface, this seems like an almost trivial problem. Capture the referrer, and send you back there after you’re done. Unfortunately, it wasn’t that simple.

The Curveballs

No Sessions

The initial idea for all of this was to simply capture the value in the session and hold on to it until we were ready to use it. Unfortunately, we’d need to clear that session variable whenever we finally did use the value. Otherwise, the return value could very easily be incorrect if you follow a different course of action and we didn’t reset it. This seemingly small limitation led to a much more advanced, but ultimately elegant solution.

A screenshot of the link for returning to the issue listing. Figure 2 From every issue detail page, you can easily return to the issue listing page and rest assured that it’s still sorted and filtered the way you left it.

Entry and Exit Points

From the issue detail page, there are actually multiple different actions you could pursue, and each complicates matters in its own unique way. Without going into the boring detail, there are 7 relevant courses of action that we need to consider. (Figure 3)

  1. Create a New Issue - It gets really hairy here because you can ultimately end up creating and adding another issue countless times, and we need to constantly keep track of your issue listing and sorting conditions.
  2. Return to Issue Listing - While this is conceptually simple, it presents a challenge in that it’s possible to arrive on the issue listing from the Dashboard. As a result “Return to Issue Listing” isn’t always accurate, and we need to dynamically check and see if the text should be “Return to Dashboard”.
  3. Edit the Issue - Editing the issue takes you to a different page, so we end up losing the value of our referrer. As a result, we have to make sure that we pass the referrer along to the edit page so that it knows where to send you after you’re finished editing.
  4. Delete the Issue - This is fairly straightforward. We need to delete the issue and send you back to your list.
  5. Delete a Comment - Thankfully, this is handled in almost exactly the same way as deleting the issue.
  6. Follow a link to a different issue - Of course, issues can link to each other, so it’s entirely possible that you might go off and look at another issue. If you do, we still need to be able to bring you back to your list.
  7. Update the Issue/Add a Comment - This is similar to deleting the issue, however, because we’re using a web form, we have to remember the URI with a hidden form field instead of passing it in the address bar.
A screenshot showing all of the relevant exit points from the issue detail page. Figure 3 There are several exit points on the issue detail page where we need to make sure that we maintain the original referring page so that we can return there.

Minimizing the Use of the Querystring

A small, but very subtle point is that we need to do all of this while relying on the query string as little as possible. Many people copy the issue link and paste it into their commit messages for source control. (Figure 4) If a query string value is used to remember the original starting point, then the link becomes cumbersome to use in a commit message.

A screenshot of a normal issue detail URL. Figure 4 Generally, the issue detail URLs are concise, but if we attached a query string value on there all of the time, they would become unwieldy.

We weren’t able to do all of this without leaning on the query string a little, but we definitely made a conscious effort to rely on it as little as possible. This definitely presented some small issues that weren’t trivial to work around.

In some cases, this may seem trivial, but it’s a crucial part of the interface design. Being able to access and share issues by copying and pasting links is going to happen. In fact, it’s going to happen a lot. So that means that clean URLs, while not always possible, are a high prioiity.

A screenshot of an example issue listing URL. Figure 5 All of the filtering and sorting is maintained via the URL. This enables you to easily share the URL with others and rest assured they will see the same thing. It also enables us to return to this page and reapply the same filters and sort.

Similarly, all of the issue listing pages are handled within the query string (Figure 5) so that they can easily be shared with other team members or bookmarked in your browser.

Missing Referrers

Finally, while we initially grab the referrer from the environment, there’s no guarantee that we’ll have a referrer. If you visit a link directly, we have to fall back on a default. So, we had to be able to specify default locations to send you if you arrived from an external link.

A photo of the sketchbook showing the quick and dirty process flows I sketched out. Figure 6 I’ve found that it’s almost always easier to solve a complex problem by starting off with a sketch of the solution.

Designing the Solution

Now that we have a firm grasp of the problem, we can dive into the steps we took to design a solution. This solution is actually the second version of “Smart Return”. The first version evolved over time as we began to recognize the edge cases, and as a result, it had plenty of room for improvement. With this version, we were able to design the solution from the ground up after we had an intimate understanding of the problem, and that was key to creating a cleaner and more maintainable solution for the future.

Some zoomed out examples of the redirects and linking scenarios. Figure 7 I created more detailed versions of the sketches in Omnigraffle where different color lines represent different relationships between the pages.

Recognizing a Pattern

This time around, I wanted to build a reusable framework that would be easy to understand and reuse throughout Sifter. So, I went through the page flows so that I could build an understanding of the entire scenario.

I started off with some quick sketches (Figure 6) of the page flows. Then, I migrated the sketches into Omnigraffle (Figure 7) for a more detailed view of the concepts and challenges.

Designing a Framework

At this point, I began to understand and see how the different pages interacted with each other. I made sure to cover all of the different scenarios and set out to describe how each page would share the return URL with the other pages. (Figure 8)

A screenshot of the key for the relationship diagrams. Figure 8 By mapping the page relationships to a key, I was able to create a simple and memorable way for the pages to communicate the return URL between themselves.

This laid out the vision of how these pages would work together, and I had a language to describe the relationships. At this point, it became almost trivial to implement the code necessary for making it happen. More importantly, it became incredibly easy to apply this logic at other points within Sifter.

Summary

One of the most significant pieces of creating a web application is understanding the interaction design. This almost invisible, yet critical piece of the design process is easily overlooked. If done correctly, it’s invisible and taken for granted. However, if handled poorly, it’s the difference between a clunky vs. a seamless experience.

Long story short, some of the most valuable elements of design are the ones that you don’t see. Just because there isn’t a visible manifestation of a decision doesn’t mean that it’s any less important.

Comments

Comments are here for discussion related to this article. If you have a comment or question not related to the article, please . Please try to keep things constructive and on-topic. Comments that are not constructive or on-topic will be deleted.

Congrats

December 09, 2008 at 02:15 AM by Tamim

At least man. What took you so long to finish it?

#

Finishing SIfter

December 09, 2008 at 09:15 AM by Garrett Dimon

Well, it’s not exactly trivial to design, build, and launch a web app while freelancing to pay the bills. :) Really though, I wouldn’t call it finished so much as I would say it’s just getting started. ;)

#

Code?

December 09, 2008 at 10:54 AM by Dusty

Hey Garrett.. Nice article, and Sifter looks great!

Any plans on releasing an implementation at all? Most code I’ve seen to deal with this doesn’t maintain clean urls, or uses the session, or doesn’t handle the edge cases correctly. Would love to see what you’ve put together…

#

Releasing the Code

December 09, 2008 at 03:56 PM by Garrett Dimon

I thought about sharing a higher level of detail with this post, but I was afraid it would muddy the waters given that most people aren’t here for code.

It might be a good candidate for a plugin in the future, but it’s not a high priority right now. :/ Honestly, the code isn’t really complex at all. The most interesting part is just taking the time to think through all of the edge cases, and those will vary dramatically for each application.

#

Nice writeup

December 09, 2008 at 04:50 PM by Jason Long

Thanks for the nice writeup, Garrett. I love these sorts of behind-the-scenes looks.

While not completely the same, I recently implemented some similar functionality to deal with browser history with dynamic pages. I went with a Gmail-style solution where I update the URL hash when altering a sort filter or navigating to a different page in the paginated results. You end up with URLs like

http://myapp.com/users#active/p3

for viewing users in the ‘Active’ tab and the third page in. I still update the session when changing the column sort order or active tab, but I only retrieve those if the hash params aren’t there when the page is requested.

I’m sure we’ll start to see more patterns and best practices for this sort of problem as dynamic webapps continue to become more pervasive.

Anyhow, keep up the great work!

#

Thank you for this

December 09, 2008 at 10:13 PM by Charlie Park

If only all webapp developers would post writeups on their thought process behind their interaction design. I know you’re probably eyeball-deep in feature requests, account servicing issues, and whatnot. So thanks for taking the time to write this up and to share it with us.

#

Cookies?

December 10, 2008 at 06:22 PM by Justin Makeig

A persistent version of the client-side state of a given resource representation, such as a list of issues, sounds like a good candidate for a cookie. With the sort and filter options stored in a cookie you’d get clean URLs and the ability to easily maintain state regardless of the path that the user takes.

#

Cookies

December 10, 2008 at 11:12 PM by Garrett Dimon

Cookies present the same challenge as using sessions. If you set a cookie, but then don’t clear it, you can end up having an incorrect value the next time you visit the page. With the session, it will at least reset when the session ends, but with a cookie, the value could be persisted even when it shouldn’t.

#

HTML isn't allowed, but Markdown is enabled.

Sifter. Hosted bug and issue tracking.
Hi. I’m Garrett Dimon, a freelance designer/developer in Dallas, TX. This is my site about people, design, and technology. I designed and built a bug and issue tracking application called Sifter. Still have questions? Feel free to .
Browse related articles tagged… design, interface, sifter

Subscribe. Proud member of 9rules. Powered by SimpleLog.