Building Faster Websites
Craig Walker, Chief Technology Officer
What is Xero?
http://www.xero.com/signup/
Performance === Usability
Performance === Money
• Bing: +2s in response time = 4.3% LOSS of revenue/user
• Google: +500ms in response time = 20% LESS traffic
• Amazon: +100ms in response time = 1% LESS sales
• Shopzilla: +5s response time led to 12% increase in revenue & 25% increase in page views
ReceiveLast Byte
Send Last Byte
Send Data
The HTTP Request
Server
Browser
Establish Connection
InitialConnection
Open Socket
Initial HTTPRequest
First Byte
ReceiveFirst Byte
Send First Byte
Content Download
ISP
Get IP
DNSLookup
5%
95%
25%
75%
Empty Cache Primed Cache
Focus on the front-end
• 75-95% of the end-user response time for Xero customers was spent on the front end
• Much easier to optimise than server side performance
• Greater potential for improvement – especially from a front-end users point of view
• MySpace's Performance Tracker msfast.myspace.com
• MS Visual Round Trip Analyzer
Lotsa tools!
• HTTPWatch www.httpwatch.com
• AOL PageTest www.webpagetest.org
The ones we use at Xero …
• Fiddler www.fiddlertool.com
• Firebug www.getfirebug.com
• YSlow developer.yahoo.com/yslow
We don't all run Windows 7 & IE8 …
Browsers
6616
29275
15596
5318
27233
Chrome IE7 IE8Safari 4 Firefox 3
Operating Systems
728
10891
20717
53065
1383
Linux MacOSXVista XPWindows 7
Fiddlerdemo
The Rules
Zip It!
• Significantly reduces download size – 60-80% saving on text based content
• 90% of browsers support compression
• Benefits users & you too:– Reduces traffic costs
– Doesn’t require code change
• Zip everything you can– html, aspx, js, css, xml, txt, json,
ashx …
Fly’s down Zipped
Turning on HTTP Compression
demo
Things to be wary of with compression …• Make sure you test – don't assume compression
• Approximately 5% of users get uncompressed responses
• Browsers (& other applications) indicate compression support by sending Accept-Encoding: gzip, deflate and responding with Content-Encoding: gzip
• Some client software (anti-virus, anti-phishing) and proxy servers can strip Accept-Encoding
• If Accept-Encoding is missing let your users know!
• Use Advanced Logging for IIS7 to track request & response headers http://www.iis.net/extensions/advancedlogging
Minify all static content
• CSS, JavaScript, XML, JSON, HTML can all be minified
• Not a replacement for gzipping but is a perfect accompaniment to it (we've seen up to 50% extra savings)
• No shortage of tools:– JSMin
– CSSTidy
– YUI Compressor
• JavaScript obfuscation can also be useful – just test that your app still works afterwards
You don't really want people to read your code do you?
Ext.DomHelper = function(){ var tempTableEl = null; var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i; var tableRe = /^table|tbody|tr|td$/i;
var createHtml = function(o){ if(typeof o == 'string'){ return o; } var b = ""; if (Ext.isArray(o)) { for (var i = 0, l = o.length; i < l; i++) { b += createHtml(o[i]); } return b; } if(!o.tag){ o.tag = "div"; } b += "<" + o.tag; for(var attr in o){ if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue; if(attr == "style"){ var s = o["style"]; if(typeof s == "function"){ s = s.call(); } if(typeof s == "string"){ b += ' style="' + s + '"'; }else if(typeof s == "object"){
Ext.DomHelper=function(){var n=null;var g=/^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;var b=/^table|tbody|tr|td$/i;var a=function(w){if(typeof w=="string"){return w;}var q="";if(Ext.isArray(w)){for(var u=0,r=w.length;u<r;u++){q+=a(w[u]);}return q;}if(!w.tag){w.tag="div";}q+="<"+w.tag;for(var p in w){if(p=="tag"||p=="children"||p=="cn"||p=="html"||typeof w[p]=="function"){continue;}if(p=="style"){var v=w["style"];if(typeof v=="function"){v=v.call();}if(typeof v=="string"){q+=' style="'+v+'"';}else{if(typeof v=="object"){q+=' style="';for(var t in v){if(typeof v[t]!="function"){q+=t+":"+v[t]+";";}}q+='"';}}}else{if(p=="cls"){q+=' class="'+w["cls"]+'"';}else{if(p=="htmlFor"){q+=' for="'+w["htmlFor"]+'"';}else{q+=" "+p+'="'+w[p]+'"';}}}}if(g.test(w.tag)){q+="/>";}else{q+=">";var x=w.children||w.cn;if(x){q+=a(x);}else{if(w.html){q+=w.html;}}q+="</"+w.tag+">";}return q;};var o=function(v,q){var u;if(Ext.isArray(v)){u=document.createDocumentFragment();for(var t=0,r=v.length;t<r;t++){o(v[t],u);}}else{if(typeof v=="string"){u=document.createTextNode(v);}else{u=document.createElement(v.tag||"div");var s=!!u.setAttribute;for(var p in v){if(p=="tag"||p=="children"||p=="cn"||p=="html"||p=="style"||typeof v[p]=="function"){continue;}if(p=="cls"){u.className=v["cls"];}else{if(s){u.setAttribute(p,v[p]);}else{u[p]=v[p];}}}Ext.DomHelper.applyStyles(u,v.style);var w=v.children||v.cn;if(w){o(w,u);}else{if(v.html){u.innerHTML=v.html;}}}}if(q){q.appendChild(u);}return u;};var k=function(v,t,r,u){n.innerHTML=[t,r,u].join("");var p=-1,q=n;while(++p<v){q=q.firstChild;}return q;};var l="<table>",e="</table>",c=l+"<tbody>",m="</tbody>"+e,i=c+"<tr>",d="</tr>"+m;var h=function(p,q,s,r){if(!n){n=document.createElement("div");}var t;var u=null;if(p=="td"){if(q=="afterbegin"||q=="beforeend"){return;}if(q=="beforebegin"){u=s;s=s.parentNode;}else{u=s.nextSibling;s=s.parentNode;}t=k(4,i,r,d);}else{if(p=="tr"){if(q=="beforebegin"){u=s;s=s.parentNode;t=k(3,c,r,m);}else{if(q=="afterend"){u=s.nextSibling;s=s.parentNode;t=k(3,c,r,m);}else{if(q=="afterbegin"){u=s.firstChild;}t=k(4,i,r,d);}}}else{if(p=="tbody"){if(q=="beforebegin"){u=s;s=s.parentNode;t=k(2,l,r,e);}else{if(q=="afterend"){u=s.nextSibling;s=s.parentNode;t=k(2,l,r,e);}else{if(q=="afterbegin"){u=s.firstChild;}t=k(3,c,r,m);}}}else{if(q=="beforebegin"||q=="afterend"){return;}if(q=="afterbegin"){u=s.firstChild;}t=k(2,l,r,e);}}}s.insertBefore(t,u);return
955KB -> 265KB
539KB -> 141KB
Reduce HTTP Requests
• Less components means a faster page
• Every request is an overhead
• Combine scripts
• Combine CSS
• Combine images into CSS Sprites
• Don’t rely on cache: 304’s are still requests
CSS Sprites
• Combine all small images into one large image
• Use CSS to control the displaying of each image
The designers want what?
11 images11 HTTP Requests3.3 KB total size
Obey your thirst®
1 image1 HTTP Request1.6 KB total size
And the code?<div class="buttons">
<span class="large green button"> <button type="button"> <span class="checkbox icon"> Approve </span> </button> </span> <span class="large blue button"> <button type="button"> <span> Save </span> </button> </span> <span class="large red button"> <button type="button"> <span class="delete icon"> Delete </span> </button> </span> <span class="medium gray button"> <button type="button"> <span class="delete icon"> Cancel </span> </button> </span> </div>
.buttons span.button { background:transparent url(buttons.png) no-repeat 0 0;}
.buttons span.button button, .buttons span.button a { background:transparent url(buttons.png) no-repeat 100% -120px;}
.buttons span.blue { background-position: 0 -30px;}
.buttons span.blue button, .buttons span.blue a { background-position: 100% -150px;}
.buttons span.red { background-position: 0 -60px;}
.buttons span.red button, .buttons span.red a { background-position: 100% -180px;}
.buttons span.green { background-position: 0 -90px;}
.buttons span.green button, .buttons span.green a { background-position: 100% -210px;}
SpriteMedemo
Optimise images• Use PNGs instead of GIFs
• Avoid alpha filters – can block rendering and freeze the browser
• PNG8 is best and supported by IE6 (yes – even with transparency
• Optimise further with tools like PNGOUT
• Make sure you have a favicon.ico:• Every browser will request it• Best not to respond with a 404• Make it small and cacheable
12KB15KB
Maximise Parallel Downloads
• Most browsers are limited to 6 connections total and 2 connections per hostname
Browser Parallel Downloads
Firefox 3.x 6
Internet Explorer 7 2
Internet Explorer 8 6
Safari 3.x 4
Safari 4 4
Chrome 4
• Increase the number of hostnames to increase the number of parallel downloads
• Don't overdo it! (DNS lookups are expensive so limit to 2-4 domains)
JavaScript external and on the bottom• Move scripts to external files for both reuse and
caching
• Promotes better script design
• Push scripts as low as possible
– Often difficult with document.write or with inline calls requiring loaded JavaScript
– Be pragmatic – think about splitting JavaScript into “must be loaded” and “can be loaded on demand”
• Scripts will block both downloading and rendering until parsed
• Remove duplicate scripts (IE has a habit of downloading them again)
Maximise the cache
• Understand the ratio of users with cached vs uncached
• Add an Expires header– Not just for images – should be used on all static content
– Set a “Never expire” or far future expires policy if you can
– Reduces HTTP requests – once component is served, the browser never asks for it again
– Date stamping in file names makes it easier
• Remove ETags– ETags are another caching mechanism – sent with every
request
– Uniquely created per web server – not good in web farms
– Just turn them off and use Expires headers instead
Use a CDN
• Content Delivery Network
• Distributes content closer to the last mile
• Distribute your static content before distributing your dynamic content
• Akamai most popular but expensive for small sites
• Xero utilises a rudimentary CDN using IP lookup to determine location
GETRequest
Response with HTML document
ImagesJSCSS
How it works:RegisterCSS("/common/style/xero.css", "screen")RegisterJavascript("/common/scripts/xero.js")
<link rel="stylesheet" type="text/css" media="screen" href="https://nzs1.xero.com/common/style/xero.css" />
<script type="text/javascript" src="https://nzs2.xero.com/common/scripts/xero.js">
</script>
Get location from IP
Reduce cookie weight
• Cookies are sent back with every request
• Keep cookie size small and only store what's required – use server-side storage for everything else
• Consider cookie free domains for static content
• And while we're at it – reduce ViewState too!
Preloading …
• Preload components you’ll need in the future
• Unconditional preload – Xero login page
preloads all core components so that the dashboard experience is better
• Conditional preload– Often based on where
you think a user might go to next
YSlow
• Firebug extension
• Grades performance – not about response times but about how well a site has adopted the techniques suggested
• Response time inversely proportional to YSlow score – get as close to A as possible to get the maximum performance gain
YSlowdemo
Aptimize
• Plugin that works with both IIS & Apache
• Merges CSS & JSS files
• Reduces & optimises images with CSS sprites & CSS inlining
• Compresses content with minification
• Improves caching
• Used by over 300 websites & intranets
• And it does all this in real-time!
www.aptimize.com
Aptimizedemo
Aptimizing Microsoft
• Microsoft used Aptimize to speed up sharepoint.microsoft.com
• HTTP requests reduced from 96 to 35
• Sped up first view, repeat view and start render by over 50%
• YSlow went from an E to a B
Things to take away
• Focus on the front-end
• Be an advocate for your users – isn't it nice to have happy users?
• Faster web sites lead to:
– Better user experience
– Reduced operating expenses
– Increase revenue
Questions?
www.xero.com
Recommended