Monday, August 19, 2013

The next time I launch a website

Over the past decade, I've launched a lot of websites. In that time, it's gotten easier and easier to scale them, especially content sites with emphasis on read queries. But there are many challenges remaining, and until we can use seamless iframes (dammit, when?), scaling page requests will require a mix of technologies to balance breadth (pushing the same content out to lots of people, usually via edge caching on a CDN) versus depth (pushing unique content out to one person, possibly via Ajax personalization of a generic page). Traditionally, we've started by making everything dynamic -- all requests hit the app servers, all responses unique, even if they have similar content. Then we layer on the cache, re-engineering and refactoring as the site grows and we discover performance bottlenecks. This approach works, but it's not optimal, because it's reactive: we wait until we observe the problem before addressing it. Possibly, we wait too long.

But there may be a better way, and it doesn't require premature optimization...

A sub-optimal route

At the AWS conference, I learned that CloudFront (Amazon's CDN) will accelerate page delivery even if nothing is cached. This is due to route optimization: requests hit Amazon's edge servers, located all over the world, and then immediately enter the AWS optimized private network, traversing fewer hops over better pipes on their way to the origin S3/EC2 sources. They don't bounce all around the 'net, trying to find their way to Virginia. (Important: this applies only if the origin is within AWS.) So, I will configure CloudFront with no caching at all -- every request will be passed to the origin for unique resolution. Effectively, there's no CDN at all, but I will get the network benefits. Importantly, though, the caching mechanism is in place from day one. Any engineering that must be done to effectively work within this infrastructure can be incorporated into the first build-out, not delayed until it starts being an issue. As traffic grows, I'll dial up the cache. Maybe just 30 seconds at first. And of course, different kinds of content can be cached for different amounts of time. Plus, individual cookies can be acknowledged or ignored, so personalized requests can be passed to the origin while generic requests are cached, even at the same URI.


Another good practice from day one is to plan for subdomains: as part of scalability, I can expect that I'll want to serve different content from different subdomains -- cdn.mysite.com, api.mysite.com, etc. It's easier to deal with this later if the code already knows about it. At first, these can all resolve to the same place. Importantly for CloudFront, I'll also need a post.mydomain.com URL to receive form POST requests, which CloudFront won't handle (AWS: please implement a feature to forward POSTs to the origin!).

[UPDATE: CloudFront now supports POSTs -- as well as PUTs and other verbs. Whoo! http://aws.amazon.com/about-aws/whats-new/2013/10/15/amazon-cloudfront-now-supports-put-post-and-other-http-methods/]

So, the next time I launch a website, I'm going to put the whole thing behind CloudFront, from day one.

Thursday, April 4, 2013

Use Your Words

An up-and-coming User Experience (UX) designer recently asked me whether it's possible to fully separate UX from visual design -- to create a "pure" UX that's only functional and not aesthetic. Good question, up-and-comer! This is a very appealing idea: the purpose of most applications is functional, whether it's selling something, or distilling information, or what have you. Therefore, the goal of UX is to facilitate the achievement of that functional objective. Aesthetics are an important but secondary concern.

To this end, many UXers employ wireframes in the design process. A wireframe is a bare-bones representation of an application screen, with only black text and boxes on a white background. Without any influence of colors, pictures, or fonts, it's meant to represent a strictly functional view -- pure UX. Frequently, faux-Latin "Lorem Ipsum" or other placeholder language is used to indicate areas of text. Desginers often present these to client stakeholders, asking for sign-off that yes, this interface is appropriate for the client's goals. Unfortunately, it's often difficult to get users to really connect with such "low-fidelity" interface mock-ups. Lacking visual excitement, the wireframe appears as a sqiggly field of grey, and users will often say "Sure, that looks fine," regardless of what you put in front of them. What can we do to grab stakeholders' attention? At this stage, we don't want to rely on fonts and colors; that puts us at risk of making something pretty but dysfunctional. The solution is words.

I've found that stakeholder engagement increases significantly when we use real words in our wireframes. Words hit the brain really fast, and they evoke all sorts of meaning to the user. So, instead of Lorem Ipsum, write wireframes with actual representative content, which makes it much easier for a client to understand how users will perceive the application. If the client hasn't provided content, then make it up! This may lead to a debate with the client, since you're putting words in his or her mouth. Good -- now is the time to have that debate. If you don't know what the content is going to be, you won't be able to help users navigate that content. Use the wireframing process as an opportunity to elicit a content strategy. Even if you do receive content, tweak it. Make it more extreme. Let your client's reaction inform your understanding of the project objectives.

Let's take an example. Here are two wireframes with generic text (click to enlarge):



To the well-trained eye, they look... um... sort of different? To a client, they're indistinguishable. If you're lucky, the reaction will be: "Sure, looks good. I'll sign off on that. Now let's see some colors!" More likely, it will be: "Why are you wasting my time with this?"

Now consider these wireframes, with specific language:



Ah, now we can see the difference. When a client signs off on one of these, that really means something. And, it gives you a big head start on visual design. Everything flows from the language.

Therefore, to the original question -- "can good UX be achieved without introducing visual design?" -- I reply: Maybe! But to a different question -- "can good UX be achieved without introducing content?" -- I say: No. So use your words! You can go a long way towards alignment to client objectives, before you break out the colored pencils.

Friday, March 1, 2013

Regarding Ruby: single-page apps vs. "Turbolinks" [updated]

The Ruby/Rails folks are taking a stand! It seems a major focus of Rails 4.0 is to empower users to create server-side applications that are as fast as client-side JS/JSON (single-pagey) apps: Rails 4.0b1

Their messaging reflects a belief that delivering dynamic HTML from the server is superior to pushing it all to the browser and communicating only via API. I think I might kind of like this. It can get messy when we cede control to the browser via single-page apps. Of course, much good work is being done here, but it's all too easy to expose security risks, or just business-logic inconsistencies, when the content doesn't really exist until the browser chooses to create it. And with today's browsers pushing updates every other minute... oof. Future-Dave, let's keep an eye on this one.

Update: I spoke to a friend who's a Ruby expert and has a very practical brilliance. He's not a fan of Turbolinks, and predicts it will be a massive failure. DOA?

Thursday, January 24, 2013

The way dates should be

Date formatting has long been a source of confusion for engineers and humans alike. In the U.S., the human standard is M/D/YY, while in the E.U., it's D/M/YY. IMHO, the euro standard is slightly better, because it progresses from smallest to largest, but it's still flawed. For one thing, it's often indistinguishable from the U.S. format: does 3/1/13 indicate January 3rd, or March 1st? Of course, if everyone used the Euro standard, then the ambiguity would be resolved, but my biggest complaint would remain: sorting. Both of these formats cause problems when sorted by a computer (for example, when sorting a directory of files by name). With either, March 3 2012 will be listed after January 1 2013. Fortunately, a solution is at hand.

So, here it is: the way dates should be:
YYYY-MM-DD

For example: 2013-01-24. Note the leading zero on the month (and this would apply to the day as well, if less than 10). This date format is unambiguous and will always be sorted correctly. I've taken to writing dates this way even in pen on paper, but I fear my one-man Occupy Date Format action will take a long time to catch fire. Still, I'm hoping to win converts. Meanwhile, I'm concerned with a more pressing debate over the order of things: the "R" and the "E" in the word "theater." Since TheaterMania (US) acquired WhatsOnStage (UK), we have divided opinions over whether it should be "theater" or "theatre." Again, there is a solution, and there is only one way the spelling of this word should be: we simply adopt Web 2.0 rules, and spell it theatr. Done and done.

Wednesday, November 21, 2012

The Software Bathtub Curve


(Disclaimer: please do not use software in the bathtub.)

Professor Hirsch speaks frequently about the bathtub curve observed in semiconductor failure rates. A microchip (unlike, say, a pair of scissors) doesn't follow a simple pattern of gradually wearing out over time until it fails. Instead, the observed likelihood of failure follows three distinct phases: First, a brief initial period when failure rates are high, mostly caused by manufacturing defects. Then there's a long steady period when failure rates are very low. Then, eventually, the physical media starts to break down, and increasing failures result. The shape of the graph looks like a bathtub.

Semiconductor failure rates

A case can be made that software follows a bathtub curve, too. However, rather than failure rate, quality is measured in terms of user satisfaction (always!). In the initial phase (beta and early release), bugs abound, performance is slow, and the application lacks refinement, having not yet benefited from extensive user feedback. Satisfaction is low but increasing. Then, after a few releases to polish it up, users settle into a comfortable working relationship with the app, and satisfaction is consistently high. Eventually, though, due to evolving user needs or technology environment, the software no longer does the job and must be EOL'd or (preferably) upgraded. Differences in methodology will affect the scale of this curve, both in terms of time and application scope: in an agile process, the cycle will run faster and across smaller portions of functionality. But the pattern will be observed nonetheless: a (hopefully) rapid resolution of initial shortcomings, followed by a comparatively longer period of stability.

Software user satisfaction

It's important to remember this when rolling out new software features: you may be tempted to make a big splash by announcing a new release loudly and to all the most important users (the ones who have been demanding those features the most adamantly). Unfortunately, this creates the biggest exposure right when the software is at its most vulnerable. Better is to take a gradual approach, allowing the new functionality to "burn in" under safe conditions, with sympathetic users and in non-mission-critical situations. When problems are found, they can be addressed calmly and efficiently. As the software matures, the size of the user base is allowed to grow. It's true that this approach lacks zazz, and may therefore pose challenges for marketing and sales objectives (so compromises must be made). But over the long haul, I've found that a cautious approach to deployment creates higher user satisfaction overall -- and that drives customer loyalty, which is good for everyone.


Wednesday, November 14, 2012

A CDN of one

On theatermania, we recently had a good-but-scary thing happen: a particular piece of content "went viral." (Is that still a thing?) It involved politics, sexuality, and musical theater, so the blogosphere lit up like crazy. Traffic shot up to 5-10x normal, with all of the new traffic hitting that one page. Now, we've got some nice caching mechanisms in place, and most assets are served by a CDN, but we're still working toward infinite* scalability. For now, all page requests involve at least one database round-trip, and the db was getting hammered with increasing intensity.

So I had a bit of an idea. There was only the one page lighting up like that, so we loaded it in a browser, copied the HTML source, and saved it to a file. Then we uploaded that file to a directory of static content, and added a single matching pattern -- that page's precise URL -- to the .htaccess file. Apache short-circuited those requests right to the saved source and bypassed all dynamic processing. The requests were still all hitting the app servers, but they required hardly any effort to fulfill. Most of the dynamic content is loaded via third-party Ajax calls (e.g. disqus), so it was still updating in real time, and for anything that was processed server-side, it was okay if it only got updated when we manually refreshed the page source.

It's not a scalable strategy for scalability: too much human intervention, and it only works if you catch a spike on the upswing. It's a quick-and-dirty thing, and not a reason to slow down at all on implementing a true scaling mechanism. But I'm going to keep it filed away in my bag of tricks just in case.

Tuesday, October 23, 2012

Proposing a new HTTP request header: max_timeout

I'm certainly not going to confess that my sites sometimes have technical problems. But a... friend...of mine tells me that servers can get bolloxed up. With a site like OvationTix (er, I mean: OshmationShmix), the most likely source of a site-wide problem will be congestion in the database. In such instances, the site will not be completely inaccessible, but it will be seriously slow. Users will say "the site's down," while engineers will say, "it's not down, exactly..." Situations like this can be frustrating, and become especially problematic when dealing with SLAs -- if pages that usually load in 2 seconds are instead loading in 2 minutes, is that "downtime," requiring remediation according to an SLA? I've seen some contracts stipulating that all pages must respond in under 3 seconds. That's good, but still too coarse-grained; what about a page (like a big report) which typically takes 20 seconds -- and which users expect will take a long time?

For this and other situations, I'm proposing a new HTTP request header: max_timeout (in milliseconds). After the timeout is reached, the browser will treat it as failed and inform the user accordingly. For users, this resolves ambiguity: currently, if a page loads slower than they're used to, they don't know whether it's likely to load in another second or two, or if it's stalled forever. For servers, this timeout can be used to abort processing of requests that exceed the threshold. (If the user isn't waiting for the response, there's no reason to keep executing the request.) Servers could also use this parameter to prioritize incoming requests: those that expect a subsecond response will go to the top of the pile, and those that say "hey, no rush" can be delayed.

How would the timeout be specified? We need to allow different requests to specify different timeouts. For the first request to a site, the timeout could be delivered via DNS (not saying this is easy to implement, but...): a response from a DNS server would contain the destination IP as well as the recommended timeout header to request. For subsequent pages, a parameter on an <a> tag or Ajax call would instruct the browser on how to construct the request header.

If a critical mass of sites/browsers/servers implement this request header, users will come to trust that the Internet won't leave them hanging -- if a server is taking longer to respond than it's supposed to, we'll time out the response so they can go about their business. Or, we could offer a prompt: "Your request has been cancelled because the website did not respond in a timely manner. Would you like to try again with no time constraint? [Yes|No]" Either way, having the confidence of a time limit would discourage users from abandoning early, or hitting "refresh" because the site feels slow. To that end, browsers could even refuse to re-issue a command until the timeout was reached. (Hang on, we're working on it...)