tag:blogger.com,1999:blog-21660147824149943462024-03-13T20:39:44.487-07:00Notes to my future selfEveryone wishes they could write a note to their past self. Well, it's not going to happen. But sometimes a note to the future can come in handy, too. So here are some things that I want my future self to know. You're welcome, future-Dave.Anonymoushttp://www.blogger.com/profile/01848587029920666466noreply@blogger.comBlogger18125tag:blogger.com,1999:blog-2166014782414994346.post-83288536350717240952015-09-09T09:22:00.003-07:002015-09-09T09:22:39.026-07:00RememberRemember, the people who use your software are people.Anonymoushttp://www.blogger.com/profile/01848587029920666466noreply@blogger.com0tag:blogger.com,1999:blog-2166014782414994346.post-53001095771478176122014-01-08T16:28:00.002-08:002014-01-08T16:29:37.528-08:00Stanke's first agile incompleteness theoremI believe in agile. Who doesn't? But I'm also vexed by it. Who isn't? I was at a great presentation on agile product management this morning by <a href="http://www.joshuaseiden.com/" target="_blank">Josh Seiden</a>, and it helped me develop some thoughts around the friction that can arise between agile and business. I feel the root issue isn't a concern about process, but about results. Specifically, about defining when a project is "done."<br />
<br />
<h4>
Here's an imagined conversation:</h4>
<div>
<br /></div>
<div class="p2">
<b>Client/stakeholder:</b> "Okay, so you're going to work on this project for a while, and when it's all done, you'll deploy it."</div>
<div class="p2">
<br /></div>
<div class="p1" style="text-align: left;">
<b>Technologist:</b> "Actually, we'd like to do this the Agile way: we'll do a series of small Sprints, and deploy frequently, analyzing and iterating as we go."</div>
<div class="p2">
<br /></div>
<div class="p1">
<b>Client: </b>"Okay, sure. Whatever. You'll do your little sprints, and deploy frequently, until the project's all done."</div>
<div class="p2">
<br /></div>
<div class="p1" style="text-align: left;">
<b>Technologist: </b>"Oh no. The project will never be done."</div>
<div class="p2">
<br /></div>
<div class="p1">
<b>Client: </b>"Say what now?"</div>
Anonymoushttp://www.blogger.com/profile/01848587029920666466noreply@blogger.com3tag:blogger.com,1999:blog-2166014782414994346.post-62638500496627785462013-11-18T19:05:00.000-08:002013-11-18T19:05:00.268-08:00QA Q&A<div style="background-color: white;">
<span style="font-family: inherit;">I was recently asked for my opinion about the trend toward eliminating <a href="http://en.wikipedia.org/wiki/Software_quality_assurance" target="_blank">QA</a> as a unique team, instead merging it into the responsibility of the engineers. I</span><span style="font-family: inherit;">'ve had experience with both structures, and success with both. I'll try to offer a balanced opinion:</span><br />
<b style="font-family: inherit;"><br /></b>
<br />
<h3>
<b style="font-family: inherit;">Traditional QA</b></h3>
<span style="font-family: inherit;">The traditional arrangement is to have completely distinct engineering and QA teams; they may even work for different companies, in different countries.</span><br />
<span style="font-family: inherit;"><br /></span>
<span style="font-family: inherit;"><b>Pro</b></span><br />
<br />
<ul style="font-family: inherit;">
<li><span style="font-family: inherit;">QA staff really knows QA</span></li>
<li><span style="font-family: inherit;">Engineers (who are more costly than QA staff) spend all their time writing code</span></li>
<li><span style="font-family: inherit;">Peace of mind: independent auditors are validating software before launch</span></li>
</ul>
<b style="font-family: inherit;">Con</b><br />
<ul style="font-family: inherit;">
<li><span style="font-family: inherit;">Engineers are allowed to be lazy (they "throw their code over the wall" and aren't responsible for making sure it works)</span></li>
<li><span style="font-family: inherit;">Increased cycle time and waste work: bouncing issues back and forth takes time and communication effort. Plus, lazy engineers introduce more bugs which then take time to fix.</span></li>
</ul>
<br />
<h3>
<b style="font-family: inherit;">Implicit QA</b></h3>
</div>
<div style="background-color: white;">
<span style="font-family: inherit;">A recent movement aims to do away with formal QA teams, and make engineers responsible for their own software quality. I'll call it "implicit QA."</span><b style="font-family: inherit;"><br /></b>
<b style="font-family: inherit;">Pro</b><br />
<span style="font-family: inherit;"></span><br />
<ul>
<li><span style="font-family: inherit;"><span style="font-family: inherit;">Engineers assume ownership of software quality, so they're motivated to get it right the first time</span></span></li>
<li><span style="font-family: inherit;"><span style="font-family: inherit;">Rapid development cycles: no waiting for QA validation before code can be deployed</span></span></li>
<li><span style="font-family: inherit;"><span style="font-family: inherit;">Users are engaged and their feedback is more relevant</span></span></li>
</ul>
<span style="font-family: inherit;"><b>Con</b></span><br />
<ul><span style="font-family: inherit;">
<li><span style="font-family: inherit;">More defects are released to production<i> (Maybe! I haven't systematically studied this. Does anyone have data?)</i></span></li>
<li><span style="font-family: inherit;">Requires engineers to wear many hats and take more responsibility; some may balk</span></li>
</span></ul>
<span style="font-family: inherit;">
</span><b style="font-family: inherit;"><b style="font-family: inherit;"><br /></b></b><br />
<h3>
<b style="font-family: inherit;">Philosophical Distinction</b></h3>
<span style="font-family: inherit;">The only true measure of software quality is user satisfaction. The question is, to what extent can that be predicted before shipping? Traditional QA uses defect counts as the proxy for user complaints, and refuses to ship until all defects are eliminated. Implicit QA asserts that only users can reliably report their own concerns, and rather than trying to catch every defect before shipping, aims to optimize the process of finding and addressing bugs in the wild. You can hear echoes of the Waterfall vs Agile debate here. </span><br />
<b style="font-family: inherit;"><br /></b>
<br />
<h3>
<b style="font-family: inherit;">Situational Considerations</b></h3>
<span style="font-family: inherit;">An important factor in this decision is the impact that a defect would have if it's released to the wild. The severity of a bug can be placed on a spectrum: </span><br />
<blockquote class="tr_bq">
<span style="font-family: inherit;">life-threatening > threat to software company's business > threat to individual client's business/mission > major user inconvenience > minor user inconvenience</span></blockquote>
<span style="font-family: inherit;">This magnitude, divided by the resolution speed, yields the impact of a bug in the wild. In aggregate, the potential impact of all potential bugs indicates the risk presented by low software quality. The higher this risk, the more attention needs to paid to QA. This attention can be paid either to reducing the number of bugs (traditional QA) or to improving resolution time (implicit QA). Or, both. Both philosophies encourage improvements of both of these dimensions, but each philosophy prioritizes one over the other.</span><br />
<b style="font-family: inherit;"><br /></b>
<br />
<h3>
<b style="font-family: inherit;">Making the problem smaller</b></h3>
<span style="font-family: inherit;">In either model, the number of bugs in production and the risk therein can be significantly reduced through automated testing. All modern languages have extensive frameworks for automated testing, against everything from low-level APIs up through front-end UX. The more extensive the test coverage, the fewer bugs make it out of dev. Test-Driven Development (TDD) is the ultimate expression of this approach, but isn't strictly necessary for achieving good test coverage. Pair programming also helps, by having two sets of eyes on the code before it even gets committed.</span><br />
<b style="font-family: inherit;"><br /></b>
<br />
<h3>
<b style="font-family: inherit;">Recommendation</b></h3>
<span style="font-family: inherit;">Either approach is good, and the choice of methodology is less important than hiring good people (devs and/or QA) and aligning them to the mission. Any organization should choose an approach that makes sense for the mission, integrates well with the engineering process, and feels right for the user relationship. Still, I usually </span><span style="font-family: inherit;">lean toward the "implicit" approach, heavily mitigated through automated testing. You'll get faster release cycles and more rapid feature evolution, and a closer connection to the users. Nobody likes bugs, but (IMHO) most communities can tolerate a few defects in exchange for rapid innovation.</span><br />
<div class="MsoNormal" style="color: #500050; font-family: 'Times New Roman', serif; font-size: 12pt; margin: 0in 0in 0.0001pt;">
<u></u></div>
</div>
<div style="background-color: white; color: #500050; font-family: arial, sans-serif; font-size: 13px;">
<div class="MsoNormal" style="font-family: 'Times New Roman', serif; font-size: 12pt; margin: 0in 0in 0.0001pt;">
<u></u><u></u></div>
</div>
<div style="background-color: white; color: #500050; font-family: arial, sans-serif; font-size: 13px;">
<div class="MsoNormal" style="font-family: 'Times New Roman', serif; font-size: 12pt; margin: 0in 0in 0.0001pt;">
<u></u></div>
</div>
<div style="background-color: white; color: #500050; font-family: arial, sans-serif; font-size: 13px;">
<div class="MsoNormal" style="font-family: 'Times New Roman', serif; font-size: 12pt; margin: 0in 0in 0.0001pt;">
<u></u></div>
</div>
<div style="background-color: white; color: #500050; font-family: arial, sans-serif; font-size: 13px;">
<div class="MsoNormal" style="font-family: 'Times New Roman', serif; font-size: 12pt; margin: 0in 0in 0.0001pt;">
<u></u></div>
</div>
<div style="background-color: white; color: #500050; font-family: arial, sans-serif; font-size: 13px;">
<div class="MsoNormal" style="font-family: 'Times New Roman', serif; font-size: 12pt; margin: 0in 0in 0.0001pt;">
<u></u></div>
</div>
<div style="background-color: white; color: #500050; font-family: arial, sans-serif; font-size: 13px;">
<div class="MsoNormal" style="font-family: 'Times New Roman', serif; font-size: 12pt; margin: 0in 0in 0.0001pt;">
<u></u></div>
</div>
<div style="background-color: white; color: #500050; font-family: arial, sans-serif; font-size: 13px;">
<div class="MsoNormal" style="font-family: 'Times New Roman', serif; font-size: 12pt; margin: 0in 0in 0.0001pt;">
<u></u></div>
</div>
<div style="background-color: white; color: #500050; font-family: arial, sans-serif; font-size: 13px;">
<div class="MsoNormal" style="font-family: 'Times New Roman', serif; font-size: 12pt; margin: 0in 0in 0.0001pt;">
<u></u></div>
</div>
<div style="background-color: white; color: #500050; font-family: arial, sans-serif; font-size: 13px;">
<div class="MsoNormal" style="font-family: 'Times New Roman', serif; font-size: 12pt; margin: 0in 0in 0.0001pt;">
<u></u></div>
</div>
<div style="background-color: white; color: #500050; font-family: arial, sans-serif; font-size: 13px;">
<div class="MsoNormal" style="font-family: 'Times New Roman', serif; font-size: 12pt; margin: 0in 0in 0.0001pt;">
<u></u></div>
</div>
<div style="background-color: white; color: #500050; font-family: arial, sans-serif; font-size: 13px;">
<div class="MsoNormal" style="font-family: 'Times New Roman', serif; font-size: 12pt; margin: 0in 0in 0.0001pt;">
<u></u><u></u></div>
</div>
<div style="background-color: white; color: #500050; font-family: arial, sans-serif; font-size: 13px;">
<div class="MsoNormal" style="font-family: 'Times New Roman', serif; font-size: 12pt; margin: 0in 0in 0.0001pt;">
<u></u></div>
</div>
<div style="background-color: white; color: #500050; font-family: arial, sans-serif; font-size: 13px;">
<div class="MsoNormal" style="font-family: 'Times New Roman', serif; font-size: 12pt; margin: 0in 0in 0.0001pt;">
<u></u></div>
</div>
<div style="background-color: white; color: #500050; font-family: arial, sans-serif; font-size: 13px;">
<div class="MsoNormal" style="font-family: 'Times New Roman', serif; font-size: 12pt; margin: 0in 0in 0.0001pt;">
<u></u></div>
</div>
<div style="background-color: white; color: #500050; font-family: arial, sans-serif; font-size: 13px;">
<div class="MsoNormal" style="font-family: 'Times New Roman', serif; font-size: 12pt; margin: 0in 0in 0.0001pt;">
<u></u></div>
</div>
<div style="background-color: white; color: #500050; font-family: arial, sans-serif; font-size: 13px;">
<div class="MsoNormal" style="font-family: 'Times New Roman', serif; font-size: 12pt; margin: 0in 0in 0.0001pt;">
<u></u></div>
</div>
<div style="background-color: white; color: #500050; font-family: arial, sans-serif; font-size: 13px;">
<div class="MsoNormal" style="font-family: 'Times New Roman', serif; font-size: 12pt; margin: 0in 0in 0.0001pt;">
<u></u></div>
</div>
<div style="background-color: white; color: #500050; font-family: arial, sans-serif; font-size: 13px;">
<div class="MsoNormal" style="font-family: 'Times New Roman', serif; font-size: 12pt; margin: 0in 0in 0.0001pt;">
<u></u></div>
</div>
<div style="background-color: white; color: #500050; font-family: arial, sans-serif; font-size: 13px;">
<div class="MsoNormal" style="font-family: 'Times New Roman', serif; font-size: 12pt; margin: 0in 0in 0.0001pt;">
<u></u></div>
</div>
<div style="background-color: white; color: #500050; font-family: arial, sans-serif; font-size: 13px;">
<div class="MsoNormal" style="font-family: 'Times New Roman', serif; font-size: 12pt; margin: 0in 0in 0.0001pt;">
<u></u></div>
</div>
<div style="background-color: white; color: #500050; font-family: arial, sans-serif; font-size: 13px;">
<div class="MsoNormal" style="font-family: 'Times New Roman', serif; font-size: 12pt; margin: 0in 0in 0.0001pt;">
<u></u><u></u></div>
</div>
<div style="background-color: white; color: #500050; font-family: arial, sans-serif; font-size: 13px;">
<div class="MsoNormal" style="font-family: 'Times New Roman', serif; font-size: 12pt; margin: 0in 0in 0.0001pt;">
<u></u></div>
</div>
<div style="background-color: white; color: #500050; font-family: arial, sans-serif; font-size: 13px;">
<div class="MsoNormal" style="font-family: 'Times New Roman', serif; font-size: 12pt; margin: 0in 0in 0.0001pt;">
<u></u></div>
</div>
<div style="background-color: white; color: #500050; font-family: arial, sans-serif; font-size: 13px;">
<div class="MsoNormal" style="font-family: 'Times New Roman', serif; font-size: 12pt; margin: 0in 0in 0.0001pt;">
<u></u><u></u></div>
</div>
<div style="background-color: white; color: #500050; font-family: arial, sans-serif; font-size: 13px;">
<div class="MsoNormal" style="font-family: 'Times New Roman', serif; font-size: 12pt; margin: 0in 0in 0.0001pt;">
<u></u></div>
</div>
<div style="background-color: white; color: #500050; font-family: arial, sans-serif; font-size: 13px;">
<div class="MsoNormal" style="font-family: 'Times New Roman', serif; font-size: 12pt; margin: 0in 0in 0.0001pt;">
<u></u></div>
</div>
<div style="background-color: white; color: #500050; font-family: arial, sans-serif; font-size: 13px;">
<div class="MsoNormal" style="font-family: 'Times New Roman', serif; font-size: 12pt; margin: 0in 0in 0.0001pt;">
<u></u><u></u></div>
</div>
<div style="background-color: white; color: #500050; font-family: arial, sans-serif; font-size: 13px;">
<div class="MsoNormal" style="font-family: 'Times New Roman', serif; font-size: 12pt; margin: 0in 0in 0.0001pt;">
<u></u></div>
</div>
<div style="background-color: white; color: #500050; font-family: arial, sans-serif; font-size: 13px;">
<div class="MsoNormal" style="font-family: 'Times New Roman', serif; font-size: 12pt; margin: 0in 0in 0.0001pt;">
<u></u><u></u></div>
</div>
<div style="background-color: white; color: #500050; font-family: arial, sans-serif; font-size: 13px;">
<div class="MsoNormal" style="font-family: 'Times New Roman', serif; font-size: 12pt; margin: 0in 0in 0.0001pt;">
<u></u></div>
</div>
<div style="background-color: white; color: #500050; font-family: arial, sans-serif; font-size: 13px;">
<div class="MsoNormal" style="font-family: 'Times New Roman', serif; font-size: 12pt; margin: 0in 0in 0.0001pt;">
<u></u><u></u></div>
</div>
<div style="background-color: white; color: #500050; font-family: arial, sans-serif; font-size: 13px;">
<div class="MsoNormal" style="font-family: 'Times New Roman', serif; font-size: 12pt; margin: 0in 0in 0.0001pt;">
<u></u></div>
</div>
<div style="background-color: white; color: #500050; font-family: arial, sans-serif; font-size: 13px;">
<div class="MsoNormal" style="font-family: 'Times New Roman', serif; font-size: 12pt; margin: 0in 0in 0.0001pt;">
<u></u></div>
</div>
<div style="background-color: white; color: #500050; font-family: arial, sans-serif; font-size: 13px;">
<div class="MsoNormal" style="font-family: 'Times New Roman', serif; font-size: 12pt; margin: 0in 0in 0.0001pt;">
<u></u><u></u></div>
</div>
<div style="background-color: white; color: #500050; font-family: arial, sans-serif; font-size: 13px;">
<div class="MsoNormal" style="font-family: 'Times New Roman', serif; font-size: 12pt; margin: 0in 0in 0.0001pt;">
<u></u></div>
</div>
Anonymoushttp://www.blogger.com/profile/01848587029920666466noreply@blogger.com4tag:blogger.com,1999:blog-2166014782414994346.post-91023530078762916422013-10-11T08:03:00.001-07:002013-10-11T08:03:08.439-07:00Why does Google serve all browser styles to all browsers?Google has long been at the forefront of Web performance: serving a quality user experience with minimal loading time. And with their new <a href="http://techcrunch.com/2013/09/19/google-makes-its-new-flat-logo-and-app-launcher-style-nav-menu-official-will-roll-out-over-the-next-few-weeks/" target="_blank">flat logo</a>, they're pushing even fewer bytes to the browser for their core search page. (That's because images with areas of flat color can be compressed much smaller than images with bevels and shadows.)<br />
<br />
So, on a whim, I checked out the source code for www.google.com, to see what else they're up to. The source is minified and therefore hard to read, but one thing jumped out at me and was a surprise: Internet Explorer-specific CSS. Of course, it makes sense that they provide styles for all browsers, but I'm using Chrome. <br />
<br />
<table cellpadding="0" cellspacing="0" class="tr-caption-container" style="clear: left; float: right; margin-bottom: 1em; margin-left: 1em; text-align: right;"><tbody>
<tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/-UahjhZOGlts/UlgQvDYplmI/AAAAAAAAAR4/2mR5grvbKm4/s1600/google+source.png" imageanchor="1" style="clear: left; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" height="256" src="http://4.bp.blogspot.com/-UahjhZOGlts/UlgQvDYplmI/AAAAAAAAAR4/2mR5grvbKm4/s320/google+source.png" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">HTML source of google.com, using Chrome browser</td></tr>
</tbody></table>
Now, before all you blog readers start a stampede (is there a word for a stampede of one?), I know it's standard practice to include all browser fallbacks in a site's master stylesheet. But, with a site at the scale of Google, and with Google's need for speed, I would expect them to read my <span style="font-family: Courier New, Courier, monospace;">user-agent</span> request header and respond with only the styles that my browser will actually use.<br />
<br />
I'm sure there's a good reason -- probably, the overhead of browser sniffing outweighs the benefit of trimming unused styles. Just curious.<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
Anonymoushttp://www.blogger.com/profile/01848587029920666466noreply@blogger.com0tag:blogger.com,1999:blog-2166014782414994346.post-91949350650134012712013-08-19T17:33:00.000-07:002013-11-18T06:22:09.200-08:00The next time I launch a websiteOver 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 <a href="http://blog.davidstanke.com/2012/09/client-side-includes.html" target="_blank">seamless iframes</a> (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.<br />
<br />
But there may be a better way, and it doesn't require <a href="http://c2.com/cgi/wiki?PrematureOptimization" target="_blank">premature optimization</a>...<br />
<br />
<table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: right; margin-left: 1em; text-align: right;"><tbody>
<tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-w2J2Ebb4-ko/UhI890TByHI/AAAAAAAAARI/iFwxijZ0ta0/s1600/LoopyTrains.jpg" imageanchor="1" style="clear: right; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" src="http://1.bp.blogspot.com/-w2J2Ebb4-ko/UhI890TByHI/AAAAAAAAARI/iFwxijZ0ta0/s1600/LoopyTrains.jpg" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">A sub-optimal route</td></tr>
</tbody></table>
<br />
At the <a href="https://aws.amazon.com/aws-summit-2013/nyc/" target="_blank">AWS conference</a>, I learned that CloudFront (Amazon's CDN) will <b>accelerate page delivery</b> <b>even if nothing is cached. </b>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.<br />
<br />
<br />
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!).<br />
<br />
[<b>UPDATE:</b> CloudFront now supports POSTs -- as well as PUTs and other verbs. Whoo! <a href="http://aws.amazon.com/about-aws/whats-new/2013/10/15/amazon-cloudfront-now-supports-put-post-and-other-http-methods/">http://aws.amazon.com/about-aws/whats-new/2013/10/15/amazon-cloudfront-now-supports-put-post-and-other-http-methods/</a>]<br />
<br />
So, <i>the next time I launch a website</i>, I'm going to put the whole thing behind CloudFront, from day one.Anonymoushttp://www.blogger.com/profile/01848587029920666466noreply@blogger.com3tag:blogger.com,1999:blog-2166014782414994346.post-61989708098449567582013-04-04T23:52:00.000-07:002013-04-05T06:37:56.838-07:00Use Your WordsAn 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, <a href="http://www.philkimdoes.com/" target="_blank">up-and-comer</a>! 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.<br />
<br />
To this end, many UXers employ <a href="http://en.wikipedia.org/wiki/Website_wireframe" target="_blank">wireframes</a> 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.<br />
<br />
I've found that stakeholder engagement increases significantly when we use real words in our wireframes. <a href="http://en.wikipedia.org/wiki/Stroop_effect" target="_blank">Words hit the brain really fast</a>, 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.<br />
<br />
<b>Let's take an example</b>. Here are two wireframes with generic text (click to enlarge):<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://3.bp.blogspot.com/-yon2dzU8ws4/UV3dThMYJeI/AAAAAAAAAP8/vywLSoznF6s/s1600/Version_AB.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="163" src="http://3.bp.blogspot.com/-yon2dzU8ws4/UV3dThMYJeI/AAAAAAAAAP8/vywLSoznF6s/s400/Version_AB.png" width="400" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
<div style="clear: both;">
</div>
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?"<br />
<br />
<b>Now consider these wireframes</b>, with specific language:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://2.bp.blogspot.com/-FGEY45DoPF4/UV3dchtkn6I/AAAAAAAAAQE/aHpFryLbDjI/s1600/Version_XY.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="163" src="http://2.bp.blogspot.com/-FGEY45DoPF4/UV3dchtkn6I/AAAAAAAAAQE/aHpFryLbDjI/s400/Version_XY.png" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div style="clear: both;">
</div>
<br />
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.<br />
<br />
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.Anonymoushttp://www.blogger.com/profile/01848587029920666466noreply@blogger.com6tag:blogger.com,1999:blog-2166014782414994346.post-46677304794598252892013-03-01T06:03:00.001-08:002013-03-14T05:57:28.535-07:00Regarding 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: <a href="http://weblog.rubyonrails.org/2013/2/25/Rails-4-0-beta1/" target="_blank">Rails 4.0b1</a><br />
<div>
<br /></div>
<div>
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.<br />
<br />
<b>Update:</b> 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?</div>
Anonymoushttp://www.blogger.com/profile/01848587029920666466noreply@blogger.com0tag:blogger.com,1999:blog-2166014782414994346.post-47337035032781164382013-01-24T18:35:00.000-08:002013-02-27T09:07:32.538-08:00The way dates should beDate 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 <i>everyone</i> 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 <i>after</i> January 1 2013. Fortunately, a solution is at hand.<br />
<br />
So, here it is: <b>the way dates should be:</b><br />
<span style="font-size: large;">YYYY-MM-DD</span><br />
<br />
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 <a href="http://www.theatermania.com/london-theater/news/01-2013/theatermaniacom-inc-acquires-uk-based-media-compan_64102.html" target="_blank">TheaterMania (US) acquired WhatsOnStage (UK)</a>, we have divided opinions over whether it should be "theater" or "theatre." Again, there is a solution, and there is only <b>one way the spelling of this word should be</b>: we simply adopt Web 2.0 rules, and spell it <b>theatr</b>. Done and done.Anonymoushttp://www.blogger.com/profile/01848587029920666466noreply@blogger.com1tag:blogger.com,1999:blog-2166014782414994346.post-81563989972763089192012-11-21T03:42:00.000-08:002012-11-21T11:16:18.772-08:00The Software Bathtub Curve<br />
<i>(Disclaimer: please do not use software in the bathtub.)</i><br />
<br />
<a href="http://www.linkedin.com/pub/corey-hirsch/4/24a/b14" target="_blank">Professor Hirsch</a> speaks frequently about the <a href="http://www.weibull.com/hotwire/issue21/hottopics21.htm" target="_blank">bathtub curve</a> 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.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-F2chDrpaOao/UKVVtq9H93I/AAAAAAAAAN0/L0-mZX8Sl4c/s1600/semiconductor.png" imageanchor="1" style="clear: right; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" src="http://1.bp.blogspot.com/-F2chDrpaOao/UKVVtq9H93I/AAAAAAAAAN0/L0-mZX8Sl4c/s1600/semiconductor.png" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><b>Semiconductor failure rates</b></td></tr>
</tbody></table>
<br />
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 <a href="http://en.wikipedia.org/wiki/End-of-life_(product)" target="_blank">EOL</a>'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. <br />
<br />
<table cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-9us_Nou6Btg/UKVV7D3DjeI/AAAAAAAAAN8/iKfsKdMUT6U/s1600/software.png" imageanchor="1" style="clear: left; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" src="http://1.bp.blogspot.com/-9us_Nou6Btg/UKVV7D3DjeI/AAAAAAAAAN8/iKfsKdMUT6U/s1600/software.png" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><b>Software user satisfaction</b></td></tr>
</tbody></table>
<br />
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.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://4.bp.blogspot.com/-pWaZeRFgcw4/UKVYdELJxfI/AAAAAAAAAOM/mjPYOVYbjB4/s1600/gradualrollout.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://4.bp.blogspot.com/-pWaZeRFgcw4/UKVYdELJxfI/AAAAAAAAAOM/mjPYOVYbjB4/s1600/gradualrollout.png" /></a></div>
<br />Anonymoushttp://www.blogger.com/profile/01848587029920666466noreply@blogger.com1tag:blogger.com,1999:blog-2166014782414994346.post-56931802389920187112012-11-14T19:01:00.000-08:002012-11-14T20:33:07.802-08:00A CDN of oneOn 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 <a href="http://blog.davidstanke.com/2012/07/cache-cachet.html" target="_blank">infinite* scalability</a>. For now, all page requests involve at least one database round-trip, and the db was getting hammered with increasing intensity.<br />
<br />
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.<br />
<br />
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.Anonymoushttp://www.blogger.com/profile/01848587029920666466noreply@blogger.com2tag:blogger.com,1999:blog-2166014782414994346.post-89546970327983270932012-10-23T23:38:00.000-07:002012-10-23T23:38:00.698-07:00Proposing a new HTTP request header: max_timeoutI'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 <i>down</i>, exactly..." Situations like this can be frustrating, and become especially problematic when dealing with <a href="http://en.wikipedia.org/wiki/Service-level_agreement" target="_blank">SLA</a>s -- 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?<br />
<br />
For this and other situations, I'm proposing a new <a href="http://en.wikipedia.org/wiki/List_of_HTTP_header_fields" target="_blank">HTTP request header</a>: 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.<br />
<br />
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 <a href="http://en.wikipedia.org/wiki/Domain_Name_System" target="_blank">DNS </a>(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.<br />
<br />
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...)Anonymoushttp://www.blogger.com/profile/01848587029920666466noreply@blogger.com0tag:blogger.com,1999:blog-2166014782414994346.post-35355503081522383342012-09-26T23:09:00.000-07:002012-10-16T11:41:01.974-07:00Client side includesHere's a future tech to keep an eye on: the "seamless" attribute for iframes. (<a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-iframe-element.html#attr-iframe-seamless" target="_blank">WhatWG spec</a>) It basically acts as a client-side include for HTML code (and so, really, isn't much like a frame at all). So, just as we've always done server-side, we can now ("now," meaning "lord knows when"... browser support ugh) break a page into fragments and serve each portion from a different URI, which are recombined by the browser. This gives us great control over caching and distribution. We could, for example, use a frame for the navigation that is served from a CDN and refreshed only every 24 hours, and one for an article content that caches for one hour (in case of updates), and one for social media stuff that is never cached. Unlike with regular iframes, all of this can happen within one DOM space, sharing styles, scripts, etc.<br />
<br />
Of course, much of the same can be achieved with javascript templating and AJAX fetches. But that's not always appropriate, and it always adds an additional layer of complexity that might be overkill. This is a conceptually simpler approach, and I like having it as an option. Unfortunately, we don't really have it as an option yet. There's no browser support; it's not even mentioned yet on <a href="http://caniuse.com/#search=seamless" target="_blank">caniuse</a>. So, future-Dave: let's watch this one as it emerges. It looks like a good trick for scalability on sites where the content on a page has a mix of freshness requirements. (And isn't that pretty much every site?)Anonymoushttp://www.blogger.com/profile/01848587029920666466noreply@blogger.com0tag:blogger.com,1999:blog-2166014782414994346.post-17420142106937820012012-09-08T11:34:00.000-07:002012-09-13T13:57:19.614-07:00Defaulting on performanceIn creating a highly performant Web environment, I've frequently been hampered by the very conservative default limits that exist in many technologies. In fact, I'd say that at least 4 out of every 5 performance bottlenecks I've encountered have come not from the system being actually overwhelmed, but rather from a self-imposed limit. Systems have numerous in-built constraints on concurrency, memory usage, and many other ways to prevent themselves from utilizing all of their available resources and realizing their true potential. Servers, you need a life coach. I'm here for you.<br />
<br />
Of course, these limits were placed there by very wise people with very good reasons. For example, on *NIX operating systems, the designers were considering the needs of a multi-user situation: in a university CS lab where dozens of naive and/or mischievous undergrads are sharing clock cycles of a single CPU, it's important that no one user is allowed to chew up all the resources and bring down the system. But a Web server typically runs just one user, and has much more capacity than it is configured to use by default. When you're chasing down an international superspy in your Bugatti Veyron (who hasn't been there?), you've gotta pop out the electronic speed limiter and go for it. Likewise, when your <i>NCIS</i> slash fiction goes viral (who hasn't been there?), you need to goose the config and let those servers fly.<br />
<br />
So, here is an incomplete list of some of the configuration-imposed constraints I've encountered over the years that are typically easy to relieve -- if you do it <i>before</i> the crowds arrive:<br />
<ul>
<li>Linux: iptables' ip_conntrack default database size is too small [see <a href="http://rackerhacker.com/2008/01/24/ip_conntrack-table-full-dropping-packet/" target="_blank">racker hacker</a>]</li>
<li>MySQL: max_connections default is low [see <a href="http://www.electrictoolbox.com/update-max-connections-mysql/" target="_blank">electric toolbox</a>]</li>
<li>Apache: prefork module is single-threaded [see <a href="http://serverfault.com/questions/45042/which-to-install-apache-worker-or-prefork-what-are-the-dis-advantages-of-eac" target="_blank">serverfault</a>]</li>
<li>MySQL: query cache is disabled by default [it's not always a good idea to turn it on, but when it's good, it can be very good. see <a href="http://www.mysqlperformanceblog.com/2006/07/27/mysql-query-cache/" target="_blank">mysqlperformanceblog</a>]</li>
<li>Tomcat: the JDBC default connection pool size is small [see <a href="http://people.apache.org/~fhanik/tomcat/jdbc-pool.html" target="_blank">tomcat 6 docs</a>, but also, <a href="http://people.apache.org/~fhanik/jdbc-pool/jdbc-pool.html" target="_blank">tomcat 7's new hotness</a>]</li>
<li>Java: default JVM memory settings don't take advantage of available
memory, and default garbage collection can create long "stop-the-world"
pauses [this is a deep topic, but <a href="http://mfinocchiaro.files.wordpress.com/2008/07/java-virtual-machine-neutral.pdf" target="_blank">here's an intro</a>]</li>
<li>Linux: default max open files is too low [see <a href="http://stackoverflow.com/questions/34588/how-do-i-change-the-number-of-open-files-limit-in-linux" target="_blank">stackoverflow</a>]</li>
<li>MySQL: back_log default is low [see <a href="http://dev.mysql.com/doc/refman/5.5/en/server-system-variables.html#sysvar_back_log" target="_blank">MySQL documentation</a>]</li>
<li>MySQL: innodb_thread_concurrency default is low [see <a href="http://dba.stackexchange.com/questions/2918/about-single-threaded-versus-multithreaded-databases-performance/2948#2948" target="_blank">stackexchange </a>-- though be aware, setting it to "0" (infinite) might be too much]</li>
<li>Linux: net.core.somaxconn, net.core.netdev_max_backlog are too low [see <a href="http://forum.nginx.org/read.php?11,215606,215690">nginx.org</a>]</li>
</ul>
Anonymoushttp://www.blogger.com/profile/01848587029920666466noreply@blogger.com1tag:blogger.com,1999:blog-2166014782414994346.post-58765487719109960252012-08-28T23:37:00.000-07:002012-09-07T07:27:08.247-07:00Who is your first customer, and why?More than once recently, I've found myself giving advice in the form of this question from a favorite professor: <i>"Who is your first customer, and why?" </i>This was something he posed to the class repeatedly, in the context of creating a go-to-market strategy for a startup. But these days -- or, heck, <a href="http://www.fastcompany.com/28905/brand-called-you" target="_blank">fifteen years ago</a> -- what's good for a startup is also good for a person, and any project you might do.<br />
<br />
So, it's a valuable question to ask when planning out a project: who is your first [ customer | user | reader | fraggle ], and why? It's natural and healthy to dream big and aim for a million users. But until the case is made to your investor (and yourself) that customer #1 is ready and willing, it's hard to believe that the throngs will follow.Anonymoushttp://www.blogger.com/profile/01848587029920666466noreply@blogger.com1tag:blogger.com,1999:blog-2166014782414994346.post-56784311065166375622012-08-01T08:46:00.000-07:002012-09-07T07:27:43.160-07:00Share the loadFor load-testing <a href="http://www.ovationtix.com/" target="_blank">OvationTix</a>, we've tried a few approaches over the years. The first time around, we used <a href="http://www8.hp.com/us/en/software-solutions/software.html?compURI=1175451" target="_blank">HP LoadRunner</a>, which is an enterprise-level tool with a price to match. It was pretty easy to use, and we got the data we needed, but it was too expensive to become a part of our ongoing development process. Ideally, we'll load-test every release before deploying, and I don't want cost concerns to intimidate us into holding back from deploying good code when it's ready to go.<br />
<br />
<a href="http://4.bp.blogspot.com/-Lh02ZLsU7xg/UBmxKsjmcnI/AAAAAAAAAKw/Gu44YNF37cM/s1600/Share-The-Load.gif" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" height="126" src="http://4.bp.blogspot.com/-Lh02ZLsU7xg/UBmxKsjmcnI/AAAAAAAAAKw/Gu44YNF37cM/s320/Share-The-Load.gif" width="320" /></a>So we moved to <a href="http://jmeter.apache.org/" target="_blank">jmeter</a>, running in Amazon EC2 cloud instances, which of course was cheaper. I set up some (admittedly clunky) Windows instances -- a controller and some generators -- and went to work. Again, we got the data we needed, but now the workflow was cumbersome. We had to launch the generators, hope they booted correctly, figure out their IPs, copy those back to the controller, then fire up the scripts, and then we had problems with the test data saturating the connection between the generators and the controller. It was fair, but not great.<br />
<br />
For this year, we made it our goal to have a smoothly automated system -- still based around jmeter, which we like. First, we tried <a href="http://blazemeter.com/" target="_blank">BlazeMeter</a>. It's a jmeter PaaS, which is a really cool idea and promises to take care of the infrastructure so we could focus on writing the tests. It's not bad at all, and I think we may use it in the future, but for now, the costs were higher than we wanted, there were too many limitations on usage (the price tiers control things like ramp-up time, max users etc.), and the reporting wasn't as transparent as we wanted.<br />
<br />
Finally, we found <a href="https://github.com/oliverlloyd/jmeter-ec2" target="_blank">jmeter-ec2</a>, which is a wrapper around Amazon's API that automates launching linux micro instances, deploying resources to those instances, firing up the test, and aggregating results. It's a lightweight script that runs in a shell and eliminates the need for a dedicated controller -- instead, each generator controls its own virtual users, and the condensed results are sent back to the shell, which makes for much less traffic between the instances (therefore, no saturation). The data collected isn't as deep as with the other approaches, but for our purposes, that's okay. We're mostly interested in simply finding out how many users we can throw at the site before it crashes. Since our plan is to take over the world, our target for concurrent users is currently 7,057,131,972. Wish us luck.Anonymoushttp://www.blogger.com/profile/01848587029920666466noreply@blogger.com5tag:blogger.com,1999:blog-2166014782414994346.post-2315267607383355832012-07-06T11:25:00.000-07:002012-07-06T11:25:31.642-07:00Cache CachetOne of our goals for <a href="http://www.theatermania.com/" target="_blank">TheaterMania</a> is to achieve infinite* scalability. I would like to be able to feel deeply confident that we could handle as much load as could possibly be thrown at us, because we have infinite* scalability. Why the asterisk? Because I'm only really looking to scale reads, and only reads of non-personalized data. There are, of course, ways to scale out writes and personalized reads (e.g. for logged-in users) but the nature of the application is that those are much less essential, and besides, it would be an isolated project so let's do first things first.<br />
<br />
So then, infinite* scalability: of course, it's about caching. The approach we've decided on is to render complete HTML pages and store them on a CDN. Any personalization can happen via AJAX calls; as long as those calls fail gracefully, the server handling dynamic content can crash, and the core content of the site is still live, being served by the CDN. For a lot of static content, we use Amazon S3 as a sort of cheapo CDN, but it's not really designed to serve massively parallel requests (I'm not sure what would happen if we tried), and it won't request content updates automatically from an origin server. Fortunately, true CDNs abound, and our plan is to leverage one. Next step is to comparison-shop <a href="http://aws.amazon.com/cloudfront/" target="_blank">CloudFront</a>, <a href="http://www.cloudflare.com/" target="_blank">CloudFlare</a>, and ??? (Akamai?). I'm hoping that since our needs are relatively modest -- we don't need ultra-low latency or global edge servers -- we can find one that fits our budget.<br />
<br />
Our challenge then will be to make sure we really understand the cache-manipulation API. As <a href="http://twitter.com/#!/gautamg" target="_blank">Gautam</a> told me, "when you cache complete pages, you have to be sure you have a very reliable cache-busting mechanism." Wise.Anonymoushttp://www.blogger.com/profile/01848587029920666466noreply@blogger.com1tag:blogger.com,1999:blog-2166014782414994346.post-15777353904116339522012-05-21T06:23:00.003-07:002012-05-21T06:23:37.189-07:00301 means 301Ever since I took control of my own DNS, I've been doing a lot of redirecting, bouncing people around to temporary sites, or adding special subdomains. (My host, <a href="http://dnsmadeeasy.com/">dnsmadeeasy.com</a> has a feature called "HTTP redirection records" that lets me serve the redirect straight from DNS, which is convenient.)<br />
<br />
One mistake I've made a few times, though, is using a 301 (Moved Permanently) when I should use a 302 ("Found" a/k/a Moved Temporarily). The problem with this is that because 301's are permanent, browsers are allowed to cache them. Which means that once you establish a 301, it can be very hard to undo it, if it's cached by users' browsers. 302s, meanwhile, are loose and flexible; the browser will re-request the original URI on every request, and if the redirect has been removed, or changed, the browser will detect that.<br />
<br />
So use 301s with care. Start with a 302, and make sure it works -- and make sure you really <i>really </i>want this to be permanent -- before locking it down as a 301.Anonymoushttp://www.blogger.com/profile/01848587029920666466noreply@blogger.com0tag:blogger.com,1999:blog-2166014782414994346.post-67280594671313789872012-05-15T06:13:00.001-07:002018-09-07T14:49:51.290-07:00Dave's Simple Rules<ol><li>Engage the user. Respect the user. Create an environment for collaborative discovery.</li>
<li>Complexity ≠ Sophistication. Seek <i>elegance.</i></li>
<li>Find a question. Find the answer. Share the answer. Find another question.</li>
<li>Be excellent today.</li>
</ol><br />
<div class="entry-meta"><a class="entry-date published">May 15, 2012</a></div>Anonymoushttp://www.blogger.com/profile/01848587029920666466noreply@blogger.com1