Monday, November 22, 2010

How Edmunds Got in The Fast Lane

When we set out to redesign insideline.com back in late 2008, we set big goals for ourselves. Some of those goals included creating our very own Content Management System (CMS), Publishing System and Digital Assets Management (DAM) System and in-memory Distributed Data Grid. On the user-facing front, we had the following goals:




  • Better Performance: Our pages need to be and feel fast to our users (onLoad fires in < 1.5 sec)

  • Richer Content: We need to serve larger photos, more videos and interactive components

  • Better Revenue: Needless to say, we need to increase our revenue


The Challenge









The challenge we had with our front-end goals was that insideline.com generated revenue through ad impressions. Almost all web developers know that including a 3rd-party component, including ads, on a page could potentially degrade the user experience. See Figure 1.0.


PerfVScode.png
Figure 1.0  Eternal struggle between delivering high performing pages and including 3rd-party components with the user experience hanging in the balance


We had to find a way to reconcile our metric for performance and our need to sustain and grow our ad impressions. We knew ahead of time that it wasn't going to be easy considering that the struggle between achieving optimal page performance while including 3rd-party components onto a page has been talked about frequently in the development community without any proposed solution.

The Process









Before we jumped to solutions, we started the process by taking inventory of all the 3rd-party components that we serve on our pages and by noting the specific formats in which we include them. This is what we found:




  • All 3rd-Party Components existed in either an iFrame or a JavaScript format

  • All 3rd-Party Components could be lazy-loaded, except for:


    • Components that use document.write (e.g. Double Click ads)

    • Components that depend on DOM events like onDOMReady or onLoad (e.g. Omniture, Brightcove)










Our immediate response to that was, "We're going to hack all 3rd-party components to make them all lazy-loadable." So we tried the following:




  • Overriding document.write: Cache the output of document.write and then place it on the page when you're ready.

  • iFrame'N'Copy: (A term we coined) Load the 3rd-party component in an empty page that lives on our domain within an iFrame and then copying the content of that page when it's done loading.









Both techniques were successful in certain situations and in certain browsers. However, they were not viable solutions that we would implement on any of our sites in Production.


Then, we had an epiphany that later helped us form an approach toward 3rd-party components and page performance. The revelation was:


Anything You Cannot Lazy-Load Should Be Treated as a Black Box.


This concept might seem trivial to some but it was crucial for us to move forward.

Our Approach









We decided that in order to deliver fast pages that serve 3rd-party components, we need to follow the following three principles:




  1. You Can't Control It All: Our attempt to force some of our 3rd-party components into the "lazy-load box" was a humbling experience

  2. Speed Up What You Can: If we were going to treat things that are out of our control as black boxes, we needed to ensure that things we have control over are lightening fast

  3. Defer Everything You Can't Control: If it's a block box, we'll make sure it is the last thing that is called on the page before it's done loading.









These principles saved us a lot of time and effort. They also helped us develop a JavaScript Loader, which works as follows:


  1. A PAGESETUP object (lightweight static object) is created at the top of the page to act as a placeholder for everything that needs to be loaded on the page.

  2. As the page gets parsed, each component gets to register the following with PAGESETUP:


    1. File dependencies

    2. Code snippet to be executed

    3. Order of execution



  3. The Loader Core logic is included at the bottom of the page and as soon as it's parsed and rendered, it does the following:


    1. Go through PAGESETUP list of file dependencies and add them onto the page asynchronously


    2. When dependecies are loaded, loop through the code snippets and execute them in order




One of the things that the loader was designed to do is to be independent from DOM events, especially onLoad and onDOMReady. This allows us to render the page to our user as soon as the browser parses and executed the Loader Code logic that is included at the bottom of the page. We will have a follow-up post on the JavaScript Loader with code snippets and such.




Figure 2.0 demonstrates the flow in which our JavaScript Loader handles 3rd-party components.

Untitled.png









Figure 2.0  3rd-Party Component Handling in JavaScript Loader

The Results





We were able to successfully achieve our goals for insideline.com. The homepage loadtime went from 9 seconds on the old site to 1.4 seconds on the new site. With JavaScript ads on the page, the load time goes up to 1.6 seconds on average. Ads load faster on the site and the we have noticed a considerable reduction in impression discrepancies, which in turn raised our ad revenue on the site by 3%.


Throughout the process, we were able to confirm and validate the results using Firebug and its extensions YSlow, Page Speed, in addition to WebPageTest. These tools have played an invaluable role in ensuring that we deliver the best user experience without compromising our revenue.


When we applied the same principles to our main site redesign (beta.edmunds.com,) we were able to cut the loading time in half. We have also received initial results that indicate that our total page views per session have increased 17%. Obviously that cannot be totally attributed to page performance since we did a major redesign of the site as well, but we are confident that page performance helped bring that number up. Please note that right now, only 25% of our total traffic is redirected to the new beta site. A full launch will be taking place in a few months.


Also, our Page Speed score has jumped 20+points, see Figure 3.0


chart2.png
Figure 3.0 Page Speed Scores for both edmunds.com legacy and new beta site

In The Works





We are currently working on the second generation JavaScript Loader, which will streamline the process even further and ensures even more optimized performance. We are also going to open source the component for the community to use and build upon. 


Together, we'll make the web faster one site at a time :-)


P.S. We're hiring :-)



Routing Traffic with JavaScript

As we prepared to launch our new beta site at Edmunds, we had to decide how to gradually move users over without permanently locking them in and without disrupting our current infrastructure. Our plan was to start small, redirecting just a few percent of users, and to ramp up as we gained confidence in the performance and stability of the new site. 



We considered all the obvious options: a server side-only approach, network-based solutions and a mix of server and client side logic. But we eventually settled on a pure JavaScript solution. It's probably the simplest of all the approaches we considered, and it allowed us to achieve our goals with the least amount of cost, effort and risk. In this post I'll discuss our routing logic, issues we ran into and some details you'll want to think about if you plan to implement something similar.



THE BASICS


Our core traffic routing logic is straightforward. When a user visits the legacy site (i.e. the current production site,) they have an X percent chance of being allocated as a beta user. If they get lucky and end up in the pool of beta users, the script redirects them to the beta site using their current, legacy URL path and query string. The allocation decision (legacy or beta) is stored in a cookie and, on repeat visits, the script uses the stored value instead of re-allocating so the user stays pinned to the same site.



URL TRANSLATION


It might seem like reusing the legacy URL path and query string to create a beta site URL would be too simple to handle the full range of URL translation between the old and new sites -- and indeed it is. It only works because the routing script doesn't have to handle anywhere near the full range of possible URLs. Redirection (and thus, URL translation) only happens from landing pages and the beta site needs to handle those URLs anyway. If it didn't, we would see a nasty decrease in traffic when we switch over to the new site. Most companies will likely have the same requirement to support well-known, landing page URLs, so this simple approach is probably enough in most cases.



On the subject of URL translation, keep in mind that a client side redirect will effectively wipe out your original referrer URL and make it appear as if all the traffic to the new site's landing pages is coming from the legacy site. This makes it difficult to compare incoming traffic. The easy solution is to capture the referrer URL on the legacy page and store it in a cookie before performing the redirect. Then provide a clear API so that tracking code on the destination page can retrieve and reset the value.



RANDOM NUMBERS


Something that may be of interest to the more mathematically inclined is the quality of random number generation in JavaScript. Like many simple random functions, JavaScript's built-in Math.random() isn't really very random at all. You certainly wouldn't use it as part of a cryptographic algorithm, and we weren't even sure if it was random enough to hit a routing percentage with any degree of accuracy. We tested a few high quality JavaScript random number generators along with the built-in function and ultimately decided that Math.random() is "good enough" at the scale we're working at and with a lot less CPU overhead compared to the alternatives. Last I heard, we were achieving a routing percentage within 5 hundredths of a percent of our target.



REDIRECTING WITH JAVASCRIPT


Initially, we performed the actual redirect by assigning a new URL to window.location.href. But we soon discovered that in Internet Explorer, href assignments aren't treated as actual redirects and the URL you are trying to redirect from is added to the browser's history. This is technically correct behavior but definitely not what we wanted. Other browsers apparently interpret changing the value of window.location.href during a page load as a redirect and they leave the original page out of the history.



The net effect of IE's behavior was that if a user clicked a link to our legacy site (say, from a search results page), was redirected to beta and then hit the back button, they would "return" to the legacy landing page. The landing page would then redirect them back to the beta site again, making for less than thrilled customers and slightly confused analysts. The solution is to use window.location.replace() instead of changing window.location.href. In all browsers we tested, this keeps the original URL out of the history and makes the back button work as expected.



ONE SCRIPT TO RULE THEM ALL


So far I've only discussed how the script works when it runs on our legacy site. But we serve the exact same script from the beta site to keep all the logic and configuration in one place. The core logic is different for beta visitors (it doesn't redirect and it makes sure that new visitors to the beta site get allocated correctly), but there is quite a bit of shared code. For instance, we store a number of routing-related attributes in a single cookie and the code that manages and wraps that cookie in an object is used by both code paths.



Another shared feature is cookie versioning. Whenever the script executes, it checks a version number stored in the beta routing cookie against the version number configured in the script. If the two don't match the script ignores the cookie and proceeds to re-allocate the user as if it was their first visit to the site. This allows us to reset the pool of beta users at any time and it provides an "escape hatch" if we want to shut down beta traffic altogether.



The script also has features to skip processing on certain internal URLs (e.g. an explicit beta site opt-in page) and to ignore requests from specific referrers. Again, these are equally useful for both legacy and beta requests.



SITE-AWARE


Since we put all of the routing logic in a single script, it needs to know which site it's being served from to execute the correct logic. Our initial solution was to have the script look at the current URL's host name to figure out if it's on the beta or legacy site. This works fine in production, where host names are stable and predictable, but it fell down quickly in our more dynamic test environments. We kept the automatic configuration logic as a fallback, but added support for a global variable to override the automatically detected site. It's a little inelegant, but it means that a template author can guarantee that either the beta or legacy "version" of the routing script will execute whenever a specific template is served. And given that the routing script is only included in one template on each site, it doesn't introduce a lot of manual overhead.



TESTABILITY


A big problem with something that's designed to act randomly is that it's, well, random -- even when it's working correctly. So we added a feature called "test actions" that allow you to request a specific, repeatable outcome by passing in a URL parameter. We added actions to force allocation to the beta or legacy site, to reset the routing cookie and to simulate manual beta site opt-in and opt-out. This has proven to be very handy for developers as well as testers.



CONCLUSION


A purely client-side mechanism to route traffic doesn't sit well with everyone. Some feel that there isn't enough real-time control over the behavior and it just feels counterintuitive to others. However, after working through a few rough spots, our JavaScript router has turned out to be a simple and effective solution. And a nice side effect is that it's very easy to get rid of.  When we switch over to the new site, we'll remove a single include from a single template and it will be gone without a trace.



If you have questions or suggestions, or if you've implemented a different kind of solution to the same problem, please tell us about it in the comments.



How Edmunds Got in The Fast Lane

When we set out to redesign insideline.com back in late 2008, we set big goals for ourselves. Some of those goals included creating our very own Content Management System (CMS), Publishing System and Digital Assets Management (DAM) System and in-memory Distributed Data Grid. On the user-facing front, we had the following goals:




  • Better Performance: Our pages need to be and feel fast to our users (onLoad fires in < 1.5 sec)

  • Richer Content: We need to serve larger photos, more videos and interactive components

  • Better Revenue: Needless to say, we need to increase our revenue


The Challenge









The challenge we had with our front-end goals was that insideline.com generated revenue through ad impressions. Almost all web developers know that including a 3rd-party component, including ads, on a page could potentially degrade the user experience. See Figure 1.0.


PerfVScode.png
Figure 1.0  Eternal struggle between delivering high performing pages and including 3rd-party components with the user experience hanging in the balance


We had to find a way to reconcile our metric for performance and our need to sustain and grow our ad impressions. We knew ahead of time that it wasn't going to be easy considering that the struggle between achieving optimal page performance while including 3rd-party components onto a page has been talked about frequently in the development community without any proposed solution.

The Process









Before we jumped to solutions, we started the process by taking inventory of all the 3rd-party components that we serve on our pages and by noting the specific formats in which we include them. This is what we found:




  • All 3rd-Party Components existed in either an iFrame or a JavaScript format

  • All 3rd-Party Components could be lazy-loaded, except for:


    • Components that use document.write (e.g. Double Click ads)

    • Components that depend on DOM events like onDOMReady or onLoad (e.g. Omniture, Brightcove)










Our immediate response to that was, "We're going to hack all 3rd-party components to make them all lazy-loadable." So we tried the following:




  • Overriding document.write: Cache the output of document.write and then place it on the page when you're ready.

  • iFrame'N'Copy: (A term we coined) Load the 3rd-party component in an empty page that lives on our domain within an iFrame and then copying the content of that page when it's done loading.









Both techniques were successful in certain situations and in certain browsers. However, they were not viable solutions that we would implement on any of our sites in Production.


Then, we had an epiphany that later helped us form an approach toward 3rd-party components and page performance. The revelation was:


Anything You Cannot Lazy-Load Should Be Treated as a Black Box.


This concept might seem trivial to some but it was crucial for us to move forward.

Our Approach









We decided that in order to deliver fast pages that serve 3rd-party components, we need to follow the following three principles:




  1. You Can't Control It All: Our attempt to force some of our 3rd-party components into the "lazy-load box" was a humbling experience

  2. Speed Up What You Can: If we were going to treat things that are out of our control as black boxes, we needed to ensure that things we have control over are lightening fast

  3. Defer Everything You Can't Control: If it's a block box, we'll make sure it is the last thing that is called on the page before it's done loading.









These principles saved us a lot of time and effort. They also helped us develop a JavaScript Loader, which works as follows:


  1. A PAGESETUP object (lightweight static object) is created at the top of the page to act as a placeholder for everything that needs to be loaded on the page.

  2. As the page gets parsed, each component gets to register the following with PAGESETUP:


    1. File dependencies

    2. Code snippet to be executed

    3. Order of execution



  3. The Loader Core logic is included at the bottom of the page and as soon as it's parsed and rendered, it does the following:


    1. Go through PAGESETUP list of file dependencies and add them onto the page asynchronously


    2. When dependecies are loaded, loop through the code snippets and execute them in order




One of the things that the loader was designed to do is to be independent from DOM events, especially onLoad and onDOMReady. This allows us to render the page to our user as soon as the browser parses and executed the Loader Code logic that is included at the bottom of the page. We will have a follow-up post on the JavaScript Loader with code snippets and such.




Figure 2.0 demonstrates the flow in which our JavaScript Loader handles 3rd-party components.

Untitled.png









Figure 2.0  3rd-Party Component Handling in JavaScript Loader

The Results





We were able to successfully achieve our goals for insideline.com. The homepage loadtime went from 9 seconds on the old site to 1.4 seconds on the new site. With JavaScript ads on the page, the load time goes up to 1.6 seconds on average. Ads load faster on the site and the we have noticed a considerable reduction in impression discrepancies, which in turn raised our ad revenue on the site by 3%.


Throughout the process, we were able to confirm and validate the results using Firebug and its extensions YSlow, Page Speed, in addition to WebPageTest. These tools have played an invaluable role in ensuring that we deliver the best user experience without compromising our revenue.


When we applied the same principles to our main site redesign (beta.edmunds.com,) we were able to cut the loading time in half. We have also received initial results that indicate that our total page views per session have increased 17%. Obviously that cannot be totally attributed to page performance since we did a major redesign of the site as well, but we are confident that page performance helped bring that number up. Please note that right now, only 25% of our total traffic is redirected to the new beta site. A full launch will be taking place in a few months.


Also, our Page Speed score has jumped 20+points, see Figure 3.0


chart2.png
Figure 3.0 Page Speed Scores for both edmunds.com legacy and new beta site

In The Works





We are currently working on the second generation JavaScript Loader, which will streamline the process even further and ensures even more optimized performance. We are also going to open source the component for the community to use and build upon. 


Together, we'll make the web faster one site at a time :-)


P.S. We're hiring :-)



Routing Traffic with JavaScript

As we prepared to launch our new beta site at Edmunds, we had to decide how to gradually move users over without permanently locking them in and without disrupting our current infrastructure. Our plan was to start small, redirecting just a few percent of users, and to ramp up as we gained confidence in the performance and stability of the new site. 



We considered all the obvious options: a server side-only approach, network-based solutions and a mix of server and client side logic. But we eventually settled on a pure JavaScript solution. It's probably the simplest of all the approaches we considered, and it allowed us to achieve our goals with the least amount of cost, effort and risk. In this post I'll discuss our routing logic, issues we ran into and some details you'll want to think about if you plan to implement something similar.



THE BASICS


Our core traffic routing logic is straightforward. When a user visits the legacy site (i.e. the current production site,) they have an X percent chance of being allocated as a beta user. If they get lucky and end up in the pool of beta users, the script redirects them to the beta site using their current, legacy URL path and query string. The allocation decision (legacy or beta) is stored in a cookie and, on repeat visits, the script uses the stored value instead of re-allocating so the user stays pinned to the same site.



URL TRANSLATION


It might seem like reusing the legacy URL path and query string to create a beta site URL would be too simple to handle the full range of URL translation between the old and new sites -- and indeed it is. It only works because the routing script doesn't have to handle anywhere near the full range of possible URLs. Redirection (and thus, URL translation) only happens from landing pages and the beta site needs to handle those URLs anyway. If it didn't, we would see a nasty decrease in traffic when we switch over to the new site. Most companies will likely have the same requirement to support well-known, landing page URLs, so this simple approach is probably enough in most cases.



On the subject of URL translation, keep in mind that a client side redirect will effectively wipe out your original referrer URL and make it appear as if all the traffic to the new site's landing pages is coming from the legacy site. This makes it difficult to compare incoming traffic. The easy solution is to capture the referrer URL on the legacy page and store it in a cookie before performing the redirect. Then provide a clear API so that tracking code on the destination page can retrieve and reset the value.



RANDOM NUMBERS


Something that may be of interest to the more mathematically inclined is the quality of random number generation in JavaScript. Like many simple random functions, JavaScript's built-in Math.random() isn't really very random at all. You certainly wouldn't use it as part of a cryptographic algorithm, and we weren't even sure if it was random enough to hit a routing percentage with any degree of accuracy. We tested a few high quality JavaScript random number generators along with the built-in function and ultimately decided that Math.random() is "good enough" at the scale we're working at and with a lot less CPU overhead compared to the alternatives. Last I heard, we were achieving a routing percentage within 5 hundredths of a percent of our target.



REDIRECTING WITH JAVASCRIPT


Initially, we performed the actual redirect by assigning a new URL to window.location.href. But we soon discovered that in Internet Explorer, href assignments aren't treated as actual redirects and the URL you are trying to redirect from is added to the browser's history. This is technically correct behavior but definitely not what we wanted. Other browsers apparently interpret changing the value of window.location.href during a page load as a redirect and they leave the original page out of the history.



The net effect of IE's behavior was that if a user clicked a link to our legacy site (say, from a search results page), was redirected to beta and then hit the back button, they would "return" to the legacy landing page. The landing page would then redirect them back to the beta site again, making for less than thrilled customers and slightly confused analysts. The solution is to use window.location.replace() instead of changing window.location.href. In all browsers we tested, this keeps the original URL out of the history and makes the back button work as expected.



ONE SCRIPT TO RULE THEM ALL


So far I've only discussed how the script works when it runs on our legacy site. But we serve the exact same script from the beta site to keep all the logic and configuration in one place. The core logic is different for beta visitors (it doesn't redirect and it makes sure that new visitors to the beta site get allocated correctly), but there is quite a bit of shared code. For instance, we store a number of routing-related attributes in a single cookie and the code that manages and wraps that cookie in an object is used by both code paths.



Another shared feature is cookie versioning. Whenever the script executes, it checks a version number stored in the beta routing cookie against the version number configured in the script. If the two don't match the script ignores the cookie and proceeds to re-allocate the user as if it was their first visit to the site. This allows us to reset the pool of beta users at any time and it provides an "escape hatch" if we want to shut down beta traffic altogether.



The script also has features to skip processing on certain internal URLs (e.g. an explicit beta site opt-in page) and to ignore requests from specific referrers. Again, these are equally useful for both legacy and beta requests.



SITE-AWARE


Since we put all of the routing logic in a single script, it needs to know which site it's being served from to execute the correct logic. Our initial solution was to have the script look at the current URL's host name to figure out if it's on the beta or legacy site. This works fine in production, where host names are stable and predictable, but it fell down quickly in our more dynamic test environments. We kept the automatic configuration logic as a fallback, but added support for a global variable to override the automatically detected site. It's a little inelegant, but it means that a template author can guarantee that either the beta or legacy "version" of the routing script will execute whenever a specific template is served. And given that the routing script is only included in one template on each site, it doesn't introduce a lot of manual overhead.



TESTABILITY


A big problem with something that's designed to act randomly is that it's, well, random -- even when it's working correctly. So we added a feature called "test actions" that allow you to request a specific, repeatable outcome by passing in a URL parameter. We added actions to force allocation to the beta or legacy site, to reset the routing cookie and to simulate manual beta site opt-in and opt-out. This has proven to be very handy for developers as well as testers.



CONCLUSION


A purely client-side mechanism to route traffic doesn't sit well with everyone. Some feel that there isn't enough real-time control over the behavior and it just feels counterintuitive to others. However, after working through a few rough spots, our JavaScript router has turned out to be a simple and effective solution. And a nice side effect is that it's very easy to get rid of.  When we switch over to the new site, we'll remove a single include from a single template and it will be gone without a trace.



If you have questions or suggestions, or if you've implemented a different kind of solution to the same problem, please tell us about it in the comments.



Thursday, November 11, 2010

Edmunds.com iPhone App Video

We launched our iPhone app back in early October. Since then, we have received great feedback from those of you who have used it. We listened and yesterday we submitted an update to the app store with much of the enhancements and improvements that you asked for.

Here's video of how our app works. Go ahead and download it now, it is FREE.







Tuesday, November 9, 2010

There is No Fighting in The War Room

Over the past few months we have been working on a data warehouse project that will basically be a relaunching of the data warehouse on a new platform. As with any project, the end is a very stressful time. The team has no option but to finish and it often leads to long hours and late nights. As part of this project, we determined that it was best to pull the team into a war room. 


What is a war room? And why are we talking about it on a data warehouse project?


Well, our war room doesn't have any of the cool things that you would imagine. We don't have a map with model tanks and soldiers and some kind of instrument to slide them around with. Also missing are faceless people behind computer screens with a beeping radar screen. Sadly, we have no giant map on the wall with beeping lights.  
In fact we don't have a single beeping graphic!  


No, our war room is just a conference room that has a bunch of lap tops, one desk phone and is surrounded by dry erase boards. However, that does not mean we are not doing some really exciting things.


The initial reaction to telling 10 people that they are going to be sitting in a room for 10 hours and forced to stare at either each other or their computers was unsurprisingly not good. People did not want to give up their desks and the quietness and privacy that comes along with that. The first day people had a really hard time with being in the room and everyone had their headphones blasting to avoid any noise or distractions. It was like we just faced everyones desks together. In fact, people were still chatting on IM instead of talking even though they were sitting no more than 4 feet from each other . 


By day 2, I started noticing that people were talking more. Instead of emailing questions, questions came up as they popped in development. Developers started collaborating and working with each other to solve problems. As the week went on, the headphones were mostly gone as people learned how to concentrate while there is noise. Questions were still being asked and gone was the waiting and slowness of back and forth through email. The team completed more work in a shorter time then they had during the whole project. 


It's been a week and it has not all been perfect. People are on edge from being locked in a room all day with others. One of the things I try to do is ask if something only impacts two people that they go to the smaller conference room next door. One of the problems is that there a lot of different personalities and naturally there will be friction. But, for the most part, what I've seen is a team that is more effective and productive. 


If we are going to continue in the future with this model, I think it will be important to come up with rules for the war room. A few suggestions:


  • Before asking a question, think - Don't just blurt out questions as soon as they pop in your head, try and solve it yourself first for a minute and then ask away

  • If a conversation only involves you and one other person, kindly use a different area to have that conversation

  • More to come...





We are still in the war room and it's not perfect in here but so far things are working and we are getting our project done. I hope it continues and that we can continue to experiment with things like this in the future. 


Updates to follow once project is complete.




Monday, November 1, 2010

How to Make Useful Paper Prototypes

I have a confession to make. While I am a BIG supporter of Design Thinking and user centered product development, I was not a big fan of the practice of paper prototyping. Paper prototyping, the act of creating complex graphical user interfaces by handwritten means, was not something I thought to be real world and particularly useful. Take a look at the example below:

commented-bad-dog-01.jpg


I made this. It's supposed to represent a web page that contains a scrollable grid of data points and allows you to filter data by checking off range parameters in the left side check boxes.


It took me about 5 minutes to make, which is the purpose of paper prototyping. You make a mock up of the GUI fast, present it to the user, get some feed back and go back to the drawing board.




It's not very good. Putting it in front of a user would give you results with marginal significance. Thus, my original aversion to paper prototypes. I figured that for the amount of time that it takes me to create a useful graphical user interface (GUI) emulation by hand, I could just as well write out a reasonable GUI in HTML.


Right?


Wrong! I was being a Mr. Know-It-All.


Take a look at this:

commented-good-dog-01.jpg


The photo above is a paper prototype that our Visual Designers made. It's a paper prototype that is useful and relevant!


OK, it's not made by a hand putting ink to paper. But, so what? It suits the purpose of paper prototyping. The work represents a reasonable GUI that can be shown to a user for feedback and subsequent revision. The fact that the Visual Designers could make it quickly on paper, without having to utilize the services of an HTML-geek makes it very cost effective. I particularly like the way that the prototype emulates horizontal scrolling. Having the paper data grid move back and forth behind another paper overlay that contains the anchored GUI elements is very inventive.


Just goes to show you what a little thought and creativity can do. The designers analyzed the need at hand, assessed the skill sets and resources available and did what had to be done, efficiently and competently. Who could ask for more? When done correctly, paper prototypes rock!


BTW: here is one of the Visual Designers. Her name is Carolyn. Her work made me rethink my attitude toward paper prototypes. She rocks!



carolyn-01.png