Amazon Really Knows How To Press My Buttons

I have to deal a lot with buttons at Amazon.  They can sometimes cause quite a bit of irritation at work, so I decided I wanted to try to change it.  Here is the proposal I wrote up for Amazon, but sadly I don’t think anyone has read it, and worst I need approval to make a change.  Let me know what you think, or if you have seen similar struggles at other companies.

Amazon Buttons

The Problem with Buttons

The button making process is quite tedious at Amazon.  Our buttons are image based, so every new button or change to a button needs to be created by our designer.  There are many different events during a project that give our designer work of creating buttons.  Our designer usually creates buttons at the start of a project, and then might add or change buttons when business requirements change throughout the project.  He will also create buttons for the different locales that our project will deploy.  Our team has 6 different locales and 2 have multiple languages.  And generally he will make additional buttons when strings get mistranslated.  For the most recent project of mine our designer has had to make 23 buttons.  This causes a lot of extra work for the designer and a lot of unnecessary downtime for the developer as he waits for new buttons.  There is also a high chance of error with this process.  It’s very easy to forget to upload a new image for a button in our less viewed locales, or forget to update an alt attribute when the button changes.  Our buttons are often inputs elements and inputs where the type attribute is equal to “image” don’t function the same across all browsers.  IE will not post the name of the input to the server where other browsers do.

The Proposed Solution

I propose we fix this button plague.  Instead of having our designers create button after button, they won’t create any.  How? By making the button text based.  Instead of having the text in the image, it will come from the HTML.  The look and feel for it to be a button will be done with CSS and one sprited image for the different types of buttons.

Pros

Eliminates Cross-browser Issues

The inputs can now be of type submit or button (whatever is needed for the page) which will eliminate the cross browser issues when it was of type image.

Localization

Because the text in the HTML is what is shown to the user, we won’t have the potential of our alt attribute being incorrect.  We also eliminate the potential of forgetting to upload or change images on the media server.   We didn’t eliminate that we might forget to change the string in some random locale, but at least now it’s more visible to spot a mistake like this.

Time (Designer and Developer)

By removing the designer as the button creator we save significant development time.  In addition to the designer just not spending time creating buttons, the developer no longer needs to fill out keystones for buttons, or wait for the button’s creation.

Performance

Using text based buttons also has performance gains.  Let’s look at the gift card ordering page as an example.  There are 11 buttons that are downloaded on page load.  Not only do these images add a lot of weight to the page they cannot be download in parallel.  For the majority of our user’s browsers; only two images can be downloaded in parallel from the same host (all 11 images are from the same host).  With the text based button there are only 2 requests instead of 11; one for the CSS and one for the image sprite.  The CSS will get bundled with other CSS on the page so there is actually only 1 additional request instead of 11.  In addition the CSS, HTML, and image sprite, needed for a text based button is much smaller than a full button image.  As a test I created a page with 11 text based buttons, and compared it to a page with 1 image based button from the gift card order page (the “proceed to checkout” button).  The total size of the text based buttons was 20% smaller than that of the imaged based button.  My test page was neither full featured nor optimized, but expect similar results when it is.

Test page with 11 texted based buttons

The total size for all components is 2.4K.  It has 3 components which are the doc a css file and one css image.

This test page just has one image from the order page

The total size for its components is 3K

Cons

Fonts

Now this method isn’t perfect.  The font on the image based buttons is Frutiger Condensed.  This is not a standard system font which means we must use other fonts to mimic the look and feel.  Although we can get a very close looking font it won’t be perfect.  There is the possibility of using @font-face to load the needed font to the browser, but there is a performance cost.

Text Based Button                           Imaged Based Button

Side by side comparrison of multiple buttons

A Look at HTML Minification for Amazon

If you take a look at any HTML source of an Amazon page you’ll notice that there is a ton of extra whitespace.  This has been puzzling me for a while.  It is important minify our JavaScript and our CSS, so shouldn’t we also minify out HTML? (Just as a note, if you look at Amazon’s CSS today you’ll notice it’s not minified, but I think I’ve convinced them to change this.)  I asked one of my coworkers on our latency team about why we don’t minify the HTML.  He told me they didn’t feel it was worth it because whitespace gets compressed really well.  I was a bit skeptical about his conclusion so I took a deeper look to see what the potentials of HTML minification are.

I took a small sample of Amazon pages (the same ones I’ve used for my other posts) and tried to remove the extra whitespace.  I went to each page and saved the HTML document to my hard drive.  I then ran the tool “HTML Compressor” on the doc.  Although “HTML Compressor” has a fancy name it only removes whitespace.  The one problem with this tool is it will also removes whitespace from whitespace sensitive elements like a PRE tag.  Luckily I didn’t see any whitespace sensitive tags, so I think it did exactly what I wanted for this test.  On average I saw a 5% decrease in the file size once gzipped.  The pages ranged from a 3.47% to 6.69% savings.  I also looked at what the savings were on the raw file, but I decided to focus on the gzipped version, because that is what the user received.  I thought 5% was a pretty good amount a savings for such a giant company.  I’m not really sure why they decided it wasn’t a big enough win when they originally looked into it.

Removing whitespace is good, but there are many more optimizations we can perform on our HTML.  Luckily for me Page Speed has a lovely new feature that will allow us to minify our HTML with more advanced optimizations.  I did the exact same experiment with the same pages and compared my results.  This time I saw an average of 10.25% savings once gzipped.  The range for this one was 7.47% to 12.07%.  Interestingly the savings for removing only the whitespace was about half, when compared with Page Speed.  Here are my results.

http://spreadsheets.google.com/pub?key=tjKHbaZ3gf9PRJdgMY1JAlg&output=html

Unfortunately even though Page Speed showed some great improvements I can go praising it quite so soon.  Although the page was smaller it actually broke things.  Here’s a look at the three different pages.

Amazon.com

Amazon.com with whitespace only minification

Amazon.com with Page Speed minification

As you can see the page that had Page Speed minify it is completely broken.  I dived a bit deeper to see if I could figure out why.  It looks like Page Speed is struggling with HTML comments embedded in script tags.  In earlier browser a developer would have to add <!– //–> around their JavaScript.  This was needed so that browsers without JavaScript would just ignore the script and move on.  <!– //–> is no longer needed and can be removed from all scripts tags.  Page Speed seems to be keeping the first part(<!–) of the comment in the HTML, but removing the second part(//–>)(at least sometimes).  This is causing a lot of extra HTML to be placed inside the script tag.  For example the link tags for the CSS are stuck in this script tag instead of the head causing none of the CSS to be loaded to the page.  Luckily there’s an easy fix.  Just remove the <!– //–>.  It’s not needed and will work exactly the same in all modern browsers (Yes that includes IE6).  By removing these comments from the page before running Page Speed, it gave me a working and minified page, as you can see here.

http://gregthebusker.com/Amazon/amazon-com-comp-edit.html

Page Speed did a great job, but I noticed that there was a bit more it could do.  They can remove comments.  The amazon.com page has comments in more places than just the script tags.  Style tags for example but I’m not sure why.  And just like the other comments they are not needed in the HTML, so might as well get rid of them.  I also noticed it wasn’t combining adjacent script or style tags.  If two style or script tags are next to one another might as well combine them into one, all you have to do is make sure to preserve order.  Has anyone else played with Page Speed’s HTML minifier?  What else could they be doing?

A Deeper Dive into Image Compression for Amazon

In an earlier post I wrote about how image compression can help Amazon.  I took a look at a few different tools available to compress images and saw some pretty significant improvements to a variety of Amazon pages.  I decided to dive a little deeper to see if I could find some tools that would compress Amazon’s images a little more.  I decide to focus on png compression.  The new found tools included advdef, DeflOpt, opting, pngout, and pngrewrite.  But the tool that was the best was pngslim.  Pngslim basically runs all the other tools listed with different parameters in an attempt to slim down every last byte from the image.

I decided to take a look at only the amazon.com page.  This page has 20 images that are pngs or gifs.  The rest of the images are jpgs of which I don’t have any update on how to make them smaller.  Those 20 images totaled to 43392 bytes.  Originally when I used smush.it to compress these 20 images I saved 984 bytes which is 2.27%.  2.27% might not seem like all that much, but we have to remember that these 20 images are the most requested assets from the Amazon servers.  One thing to note is the amazon.com page is personalized for my shopping experience.  Which means that you might not see the same 20 images that I examined.  Looking at the different images I believe that at least 17 of those images are severed to everyone.  The few images that are personalized for my shopping experience were small icons so they won’t have much weight in the overall savings.

I started by just looking at one image.  And what better image to choose than the most requested image and asset at Amazon; their sprite for the top navigation.  Smush.it was able to decrease the size of the image by 0.52%.  Which is not much, but multiplied over by millions of users everyday it adds up.  Also it only takes a few seconds to do this manually (I’m working to automate this at Amazon).

I struggled to convince people that a change this small was important, so I did some more research to see what tools could make it better.  This is where the tools listed earlier come in.  I ran pngslim on the top navigation sprite.  I tweaked the parameters a bit to try to make the image as small as possible.  The image is relatively large for a sprite so it took pngslim around 30 min to run on my personal laptop.  The savings were much more than I expected.  I was able to make the image 11.8% smaller.  It went from 6786 bytes to 5986 bytes.  Now I don’t know how many times this image gets requested, but you can trust me that it is a lot.

Some of the individuals I shared this image where were skeptical of the benefits.  They felt that the image changed fairly regularly and any improvement would be lost once the image was changed.  I did some research to see just how often this image changes.  In the last two years it has changed 11 times, with the time in between changes getting longer.  The current image was uploaded in August 2009, and before that June 2009.  Once again I don’t know the metrics, but even if the image is only up for a month and I have to redo the optimizations again I still feel it’s worth the time to upload the new image.  I do agree that automating this process would be a big improvement, but in it’s absence the extra few minutes for me every couple of months is a minimal cost.

Anyway because I got such good results on just one image I wanted to see what additional savings I could get out of all the pngs and gifs severed on the amazon.com page.  I ran pngslim on all 20 images on the page and got some great results.  I was able to shave off 6352 bytes which brought the size of the compressed set to 37040 bytes from it original size of 43392 bytes.  That’s a 14.6% savings and a lot better than the original 2.27% I saw in my first set of experiments.  Out of the 20 images only 5 were unable to be compressed.  These images also all happened to be gifs (I had 10 other gifs that I was able to compress).  The savings were all over the place depending on the image.  I had one image that I made 67.8% smaller, and one that I only slimed 2 bytes off(As an added note: In an experiment with IMDB I was able to strip off 6k on their logo sprite).

There are some great tools out there and it only takes a little bit of time to try them out.  If you know of other tools that might be able to get even better improvements let me know I’d love to try them out.

Better Rounded Corners

It seems that ever website today needs to have rounded corners.  Let’s take a look at one of the pages I currently work with; the Amazon gift card order page.  https://www.amazon.com/gp/gc/order.  Looking at this snapshot you can see there are a lot of rounded boxes in our design.  Looking at the source of the page I counted 28 instances of round box divs.  In addition there are 8 different variations of these round box divs.  This is quite a lot and it doesn’t count round boxes that are made by JavaScript interaction which can even result in an additional 2 style variations of the round boxes.  The rounded corners look nice for the design but it can be a pain to develop.  I’ve recently rebuilt the current implementation for my team’s rounded box code.  The goal of this post is to look at the new execution and show where it improves upon the current implementation.
Let’s take a look at what is currently happening.  We have a lot of different use cases where we use our round box code.  The current implementation allows for headers and footers with different colors and borders around content.  Here is a look for just one look and feel for our round boxes, which goes through most of the possible structures the round box can have.

This screenshot only shows one look and feel.  Currently we have 10 different variations to the round box style where we change border color, background color, and the exterior color of where the round box is.  Here is what our full test page looks like to include the different styles.

http://gregthebusker.com/roundbox/round-box-test-page.png

The main thing I want to look at is the image sprite for the round corners.  Here is what is currently being used.

Box Sprites

The main thing to notice is that for each of the different combination of exterior color, border color, and interior color, there is a 12px by 12px circle.  This means that if we wanted to create a new style where any of the three properties of the round box change we would have to add another 12px by 12px circle to the sprite.  It also means that each new style requires a change to the CSS and the image sprite.  My goal was to improve upon the maintainability of the system.  To do this a new sprite was created as shown here.

Box Sprites

The new sprite is clearly a lot smaller.  It is 65% smaller in both number of pixels and byte size.  Originally when I made the image I didn’t see a lot of savings by size, but I just had to run some png compression tools and I so a lot of improvements.  Awesome!  The key difference can be explained by looking at the outlined section of the two sprites below.

Old Sprite

New Sprite

These two circles may look the same, but the center of the newer one is transparent.  This allows us to no longer have a sprite dependency on the interior color of the box.  It works by allowing the background color of the surrounding div to display through to the foreground.  We can now change the background color in the CSS only and not need to create a new circle for the image sprite. This 3d view might help to visualize how the round box corners allow for the background color to display to the foreground.

Even though I had working round box html and css, I decided it would be best to scrap it and start fresh.  The concept of a round box is fairly simple.  In addition to just have some rounded corners around some content I had to incorporate the use of headers, sub headers, and footers.  The markup was fairly simple.  You start with a div to work as the background.  Then two b tags for your corners.  H2 and H3 is headers exists followed by a div to put the content.  Lastly a div to hold the footer which as a span for the footer text and two b tags for the bottom corners.  Here is what it would look like.

<div> //Background div
  <b></b>  //corner
  <b></b>  //corner
  <h2></h2> //header
  <h3><h3>  //sub-header
 <div></div>  //div for content
  <div>  //div for the footer
    <div></div> //Footer
    <b></b>  //corner
    <b></b>  /corner
    <span></span> //I'll explain this later
  </div>  //end footer div
</div>  //end background div

The markup is clean and simple for the use case.  Next is the tricky part working with the styles.  In our test page we had a few variations of the round box.  Things have to change if there are footers and/or headers.  I’m going to just talk about a round box that has a header, sub header and footer for it has the most complications.  It get the others round boxes one can easily remove/alter a few CSS classes to obtain their required look and feel.

Our round box has a header and footer where the background color is a light blue(#ebf3ff), the main content is white(#fff) and the out most border is blue(#c9e1f5).  We can quickly make classes for each of these colors.  Here is what it looks like

.RBT1 { /* RBT1 = round-box-title-style-1*/
    background-color: #ef3ff;
}
.RBC1 { /* RBC1 = round-box-content-style-1*/
    background-color: #fff;
}
.RBB1 { /* RBB1 = round-box-border-style-1*/
    background-color: #c9e1f5;
}

Now lets apply it to the HTML.

<div class="RBB1 RBT1"> //Background div
  <b></b>  //corner
  <b></b>  //corner
  <h2>Header</h2>
  <h3>Sub-Header<h3>
  <div class="RBC1">Some Content</div>
  <div class="RBT1">  //div for the footer
    <div>Footer</div>
    <b></b>  //corner
    <b></b>  /corner
    <span></span> //I'll explain this later
  </div>  //end footer div
</div>  //end background div

I also added the background color for the header to the footer div.  I shouldn’t need to do this but IE wouldn’t always display the correct color as the background of the footer so I added the same class to the footer.  IE also has a bug where it renders background colors on top of the content inside a div.  This is called by many as the peekaboo bug for scrolling down on the page will cause the browser to redraw it correctly.  There are a few ways to fix this.  I chose to add a line height to the parent div like so.

.RB {
    line-height: 1.1em;
}
<div class="RB RBB1 RBT1"> //Background div
  <b></b>  //corner
  <b></b>  //corner
  <h2>Header</h2>
  <h3>Sub-Header<h3>
  <div class="RBC1">Some Content</div>
  <div class="RBT1">  //div for the footer
    <div>Footer</div>
    <b></b>  //corner
    <b></b>  /corner
    <span></span> //I'll explain this later
  </div>  //end footer div
</div>  //end background div

Now that we have the general look and feel down lets talk rounded corners.  First lets just get the background image in place.  My rounded corners have a radius of 6 pixels, so my b tag needs to be 6 pixels wide and tall.  We also need to get the background position and position it to the right section of the circle.  Each corner will have a different position like so.

b.RBTL, /* round-box-top-left*/
b.RBTR, /* round-box-top-right*/
b.RBBL, /* round-box-bottom-left*/
b.RBBR { /* round-box-bottom-right*/
    background-repeat: no-repeat;
    background-image: url(what-your-sprite.is);
    height: 6px;
    width: 6px;
    overflow: hidden;
}
b.RBTL1 {
    background-position: 0 -24;
}
b.RBTR1 {
    background-position: -6 -24;
}
b.RBBL1 {
    background-position: 0 -30;
}
b.RBBR1 {
    background-position: -6 -30;
}

Now that we have our corners looking correctly lets start positioning them correctly.  For the top two corners we need one to go the top left and one in the top right.  We can do this simply with floats.  Floating one to the left and one to the right.  Now whenever we float a div we need to clear it so the parent div can know it’s height.  For the top two rounded corners we add a style that clears the float with the main content div.  For the bottom two corners we need a also clear their floats.  This is were that extra span comes in.  Once we float everything correctly our CSS and HTML will look like this.

b.RBTL,
b.RBBL {
    float: left;
}
b.RBTR,
b.RBBR {
    float: right;
}
.RBC, .RBS, .RBF {
    clear: both;
}
.RBS {
    display: block;
}
.RBF {
   width: 100%;
}
<div class="RB RBB1 RBT1"> //Background div
  <b class="RBTL"></b>  //corner
  <b class="RBTR"></b>  //corner
  <h2>Header</h2>
  <h3>Sub-Header<h3>
  <div class="RBC1">Some Content</div>
  <div class="RBT1 RBF">  //div for the footer
    <div>Footer</div>
    <b class="RBBL"></b>  //corner
    <b class="RBBR"></b>  /corner
    <span class="RBS"></span> //I'll explain this later
  </div>  //end footer div
</div>  //end background div

I also added a class to the footer div to give it a width of 100% and to also clear floats. Clearing floats here is makes sure that the footer starts in the right place. There were a few instances where the creater of the content forgot to clear his floats and it made things look off. The width 100% is to make sure the corners go all the way to the ends which IE didn’t always like to do.

At this point the corners are position inside of the outside border.  All we need to do is move them over a few pixels.  Because our border are 1 px in width we need to move each corner 1px up and over(down and over for the bottom two).  To do this we need to make each corner’s position relative.  Then just move it over to the correct spot.  Making the tag position: relative means that it will be placed relative to it’s normal position.  So for the top left corner we need it to move it one pixel up (top: -1px) and one pixel to the left (left: -1px).  Doing it for all the corners look like this.

b.RBTL,
b.RBTR,
b.RBBL,
b.RBBR {
    background-repeat: no-repeat;
    height: 6px;
    width: 6px;
    overflow: hidden;
    position: relative;
}
b.RBTL,
b.RBBL {
    left: -1px;
    float: left;
}
b.RBTR,
b.RBBR {
    right: -1px;
    float: right;
}
b.RBTR,
b.RBTL {
    top: -1px;
}
b.RBBL,
b.RBBR {
    bottom: -1px;
}

And just like that we are done.  Pretty simple.  Now if we want to add a new style all we have to do is change the background and border colors, plus the corner background image positions.  Once I did this I compared it to what we currently had and I saw some major improvements.  The total weight of our test page decreased by 40%, the CSS file decreased by 60%, and the new spite 65% smaller and more flexible.

We also have a JavaScript file that we use to change round box styles on the fly.  We sometime alter the round box but keep the same content.  We even have a use case where the background color has an animated color change.  Before when the rounded corners were not transparent on the inside this was not possible to do because the corner images couldn’t change gradually.  Anyway, when I rewrote a new JavaScript file for my new round box design it ended up being 36% smaller than the last one.

Feel free to use this idea on your website to build a more maintainable and bulletproof rounded box.

A Hack Comes to Life

The other day Yahoo announced richer results for search assist.  These new features provide you with the information you need before you even search.  Let’s say you’re searching for a stock quote.  For example, while you’re typing the ticker the real-time stock quote will show up directly under the search box within search assist.  The thing that I find really cool about it is that I built the first prototype.  When I was an intern at Yahoo, back in 2007, I worked with two very talented fulltime Yahoos on a project for Yahoo’s Internal Hack Day.  For those you don’t know, Hack Day is a companywide event where employees get 24 hours do create whatever they can imagine.  Our project won best hack that summer.  We built our hack before search assist existed so it was a little different.  It was centered on the idea of delivering information to the user while they typed.  We were able to show movie times, sport scores, movie star biographies and more.  Our results were a bit richer than the current implementation, but didn’t include all the hooks to yahoo properties.  If you read about a confidential project on my resume, this was the project, so I can finally talk about it now.  Check it out on Yahoo.

A Look at Extra Image Compression for Amazon

Minimizing the payload size for a high volume website is very important.  The amount of bytes sent over the wire for each server response can greatly increase the latency to your application.  One way we can minimize the payload is by making sure our images are optimized.  Two great tools for this are Google’s Page Speed and Yahoo’s Smush.It.  Once again it looks like Amazon is missing out on a fairly easy way to optimize their site.

When you start compression images there are a few things you want to keep in mind.  One is the compressions need to be lossless, so you don’t have to worry about any loss in compression.  You also want the process to be simple and automated.  At a large company you don’t know who’s uploading the images and if they understand the importance of image compression.  So the best thing to do is integrate the compression process with the upload process.  The compression will happen automatically when they upload the images to their servers.

The two tools I’m going to use for this experiment work very similar fashions to get lossless compressions.  They both attempt to crush PNGs, strip JPEG metadata, and convert nonanimated GIFs to PNGs if it makes them smaller.  I know that Smush.It will also optimize GIF animations, but I’m not sure if this was built into Page Speed.  The tools they use to do this are different, and I’m not sure which exact tools they use.  Later I’ll go into some extra details about the different command line tools available.

Let’s take a look at how much image compression can affect the payload size Amazon.  I took a look at a sample of Amazon pages for this (same ones I used for the JavaScript and CSS compression).  For each page I would wait for the page to load and then scroll to the bottom of the page.  For a few of the Amazon pages they have content below the fold of the viewport that doesn’t render until you scroll down.  I decided to do this so I could get some more images on the page to look at, although it may be more accurate if I didn’t for they might better simulate a real user’s experience.  I happen to not know what the Amazon user base does, and sadly I don’t think they would let me post it if I did(if someone at Amazon is reading this and has that data please send it my way).  After all the images were delivered to my browser I then opened up YSlow to pull starting statistics such as number of images on the page, and total image weight.  I then would run Smush.It on all the images on the page, after which I would let Page Speed analyze the page and record the results.  This is what I got.

http://spreadsheets.google.com/pub?key=tM1npIAirXo9tT24C3Z3Bqw&output=html

Using Page Speed I was able to get savings from .7 to 22 kB per page.  This ended up being less than 1% of the total image weight for 3 of the 5 pages I looked at.  One page did see a 11% reduction.  Overall this is a very small gain and probably not worth the development time to integrate into the site.  The results from Smush.It were much more promising.  4 of the pages had 6-8% in savings and one page was up to 23.55%.  Smush.It was able to strip out 12-60 kB for the total image weight, which is a substantial savings.  Looking at individual images, the percent savings was all over the place.  Some images like the image sprite for the top navigation bar only saw a 0.52% reduction in size.  Where others like the “add to order“ button on the gift card page had at 78.07% size reduction.  This wide range of individual image size reductions was consistent between the tested pages.

This actually wasn’t the first time I ran this experiment on some of the Amazon pages.  Back in September I ran some similar test and the thing that surprised me is that the numbers were a lot lower this time.  For example the first time I Page Speed on www.amazon.com I saw a 6.76% reduction on total image weight.  This time it was only .3%.  Here’s a link to my old results.  One thing to note is I only looked closely at the optimizations from Page Speed because I didn’t know how to do it easily with Smush.It.

http://spreadsheets.google.com/ccc?key=0ArWcXt0qGBf4dDlKOFJzZkU2YVVLOWJUS1hZRzhhM0E&hl=en

From my results I’ve concluded that Smush.It is a lot better than Page Speed for optimizing images.  It would be great if I could just recommend that Amazon use Smush.It for their image compression.  The problem is Smush.It doesn’t have an API for the public to use.  The thing that actually makes me really frustrated is that they used to.  The only reference to it now is a blog post by Stoyan Stefanov, one of the creators of Smush.It, which references that the API is document on FAQ page.  Unfortunately it looks like when Yahoo took over Smush.It they removed the FAQ page and the API.  The current FAQ page does say that they are working on building a public API and I think we can expect to see a command line tool eventually.

So what do we do? We could wait, but it might be a very long time for we see a public API.  Instead we can build own.  Stoyan Stefanov has a chapter in Even Faster Web Sites: Performance Best Practices for Web Developers that details all the different things you can do to optimize images.  My best advice for Amazon would be to follow Stoyan’s steps to build their own smushing tool.  Some of the command line tools that we can use to build our own script are; pngcrush(for Crushing PNGs), jpegran(for Stripping JPEG Metadata), and convert(to convert GIF to PNG).  If you want the details on how to use the tools or other options read the book or ask me in person.

A Look at Extra JavaScript and CSS Compression for Amazon

Performance is a very important aspect to worry about when building a website.  There are plenty of books and blog posts that lay out easy (and some harder) ways to improve performance of you webpage.  Having worked at Amazon for the past few months I know how important performance and latency is to company.  The thing that surprises me is even though they care so much, they have missed some really easy changes that will yield some significant improvements.

Today I’m writing about one simple change Amazon can make to improve performance on the sight.  All they have to do is compress their JavaScript and CSS more.  I say more, because Amazon already does some compression.  If you take a look at the website today, you should be able to notice, that most of the external scripts and style sheets contents have white space minification.  They have striped out all the extra white space in the file so that the file size is smaller.  In addition they gzip their components so that they are even smaller when sent over the wire.  Their already a doing a good job, but how can they make it better?  The answer is simple; use a tool to do it for you.  There are some great tools available for JavaScript compression and the ones I’m looking at today are Yahoo’s YUI Compressor and Google’s Closure Compiler.  Now let’s see what these tools can do for Amazon.

For my testing I wanted to make things as simple as possible and I wanted to make my work as little as possible.  I grabbed 5 different pages on Amazon to use for my testing.  For each page I used YSlow to pull up all the JavaScript for the page.  I then took all the external javascript files and added them to the Closure Compiler.  I then did a whitespace only optimization of the list of javascript files.  Here is the compiler code for www.amazon.com

// ==ClosureCompiler==
// @compilation_level WHITESPACE_ONLY
// @output_file_name default.js
// @code_url http://z-ecx.images-amazon.com/images/G/01/da/common/d16g-0.6.10.nl.yui._V227086409_.js
// @code_url http://z-ecx.images-amazon.com/images/G/01/s9-campaigns/s9-multipack-layout._V224259630_.js
// @code_url http://z-ecx.images-amazon.com/images/G/01/nav2/gamma/amazonJQ/amazonJQ-combined-core-5113._V228305909_.js
// @code_url http://z-ecx.images-amazon.com/images/G/01/wma/clog/core2._V241266071_.js
// @code_url http://z-ecx.images-amazon.com/media/i3d/01/swfobject-1.5.js
// @code_url http://z-ecx.images-amazon.com/images/G/01/s9-campaigns/music-player._V241742764_.js
// @code_url http://ad.doubleclick.net/adj/amzn.us.gw.atf;sz=300×250;bn=507846;u=c0b9f70f6aee437cb247bfb0130133df;ord=0YP9RZYZF99164EG5RWE;s=97;s=102;s=227;s=228;s=122;s=120;s=3;s=4;s=276;s=277;s=8;s=142;s=279;s=11;s=12;s=9;s=286;s=13;s=153;s=154;s=398;s=24;s=160;s=22;s=21;s=26;s=25;s=32;s=270;s=31;s=391;s=150;s=29;s=440;s=165;s=44;s=54;s=430;s=422;s=298;s=71;s=69;s=67;s=348;s=457;s=93;s=329;s=92;s=m1;z=3;tile=1?
// @code_url http://ad.doubleclick.net/adj/amzn.us.gw.btf;sz=300×250;bn=507846;u=0ac6c772b89f4e18872eff747f8c6d49;ord=0YP9RZYZF99164EG5RWE;s=97;s=102;s=227;s=228;s=122;s=120;s=3;s=4;s=276;s=277;s=8;s=142;s=279;s=11;s=12;s=9;s=286;s=13;s=153;s=154;s=398;s=24;s=160;s=22;s=21;s=26;s=25;s=32;s=270;s=31;s=391;s=150;s=29;s=440;s=165;s=44;s=54;s=430;s=422;s=298;s=71;s=69;s=67;s=348;s=457;s=93;s=329;s=92;s=m1;z=1;z=3;tile=3?
// @code_url http://altfarm.mediaplex.com/ad/js/10503-88221-17214-8?mpt=6277884&mpvc=http://ad.doubleclick.net/click%3Bh=v8/38e9/3/0/%2a/n%3B218941236%3B0-0%3B3%3B18273354%3B4307-300/250%3B33980052/33997930/1%3Bu%3D0ac6c772b89f4e18872eff747f8c6d49%3B%7Eaopt%3D2/1/55/2%3B%7Esscs%3D%3f
// @code_url http://img-cdn.mediaplex.com/0/documentwrite.js
// ==/ClosureCompiler==

I did this so I would just have one file to work with for each page rather than all the individual files.  Because I’m using the whitespace only optimization this one file should be roughly equivalent to the actually content being loaded to the page, or so I thought.  It turns out Amazon doesn’t minimizes all their JavaScript.  Thus the combined file I used to test already had some optimizations in it.  For www.amazon.com just this whitespace optimization compressed the file from 165.22KB to 159.81KB which is 3.27%.  This ends up to only be a 3.12% gain once gzipped.

Once I had this combined file I ran it through the YUI Compressor.  YUI Compressor has some flags that you can set like having it only minify and not obfuscate.  I decided to let it lose to do all its optimizations.  I then ran the same combined file through the Closure Compiler with simple optimizations turned on.  The Closure Compiler also has an advanced optimization option.  I’ve played with this a bit and it tends to break the code, so I left it out of my testing.  Once I had the two optimized versions I compared the results, and here is what I saw.

http://spreadsheets.google.com/pub?key=tcQVvzrVDhnT33MxfA3UZow&output=html

The most important numbers to look at here are the percent saved when gzipped columns.  The gzipped values are what get sent over the wire for Amazon affecting the client’s latency.  Using the YUI Compressor we get around 3-7 % savings on each page.  That’s pretty good for such a small action.  The Closure Compiler does even better getting savings from 8-12%.  Now one thing to remember is I cut corners to make my job easier.  I was able to get up to 7.7% savings even before I used YUI Compressor or the Simple optimizations of Closure Compiler.  This means that the numbers I’m showing undershoot the actual results of using these tools.

So what should Amazon do? Use Closure Compiler, because it has better results, right?  Not quite.  The Closure Compiler sometimes shows errors or warnings when it does compression. I didn’t see any errors in my testing but I saw plenty of warnings, which might break the code.  These warnings might not break anything but I would want to do a lot of testing to make sure.  YUI Compressor has been out for many years and has been tested and used in many development environments.  I have faith in YUI Compressor that it’s not going to break my code (Although I should still test things).  The Closure Compiler is still young and I have a feeling there are still a few things they need to tweak.  Because of this, I recommend that Amazon starts using YUI Compressor in development.  I think it makes a lot of sense for them to incorporate it into their build or upload process.  If they do it in their build process they just need to replace their minification code to use the YUI Compressor instead, which should be a trivial change.  They could also make it so that when someone uploads a file to Amazon’s media servers the YUI Compressor cleans it up before it gets saved to the server.  Either way it doesn’t affect the development process for the developers can still work with non compressed code running from their development machines.

I did some further explorations with the warning messages from Closure Compiler.  In my test I ran the code through Closure Compiler twice.  Once for the combined file and once for the simple optimized file.  The end result had a lot of warnings; 26 for www.amazon.com.  If I let Closure Compiler do the simple optimizations on the first pass I would only get 3 warnings.  This was very odd to see.  I then tried passing the combined file through YUI Compressor and then back into Closure Compiler for it’s simple optimizations.  When I did it this way I only got one warning, but it couldn’t show me what line it occurred on.  I think because the warning was on a very large number of characters into the first line it broke and wouldn’t show me any addition warnings.  I’m being a bit skeptical, but maybe it did decrease the number of warnings to 1, I’m not sure.  I think it may be worth investigating if there is a combination between using YUI Compressor and Closure Compiler to get a more compressed file that is also even more optimized.  I might run some test later where I don’t cut corners to see what results I find.

Now I’ve been skipping over CSS compression on purpose.  You cannot obfuscate CSS like you can with JavaScript so the gains are going to be much smaller.  Also there are not as many tools that work with CSS compression so I’m only going to look at what YUI Compressor can do over what is already in place at Amazon.  Stoyan Stefanov built a great tool call cssmin, which uses YUI Compressor to compress the source css.  Just like for the JavaScript compression, for CSS I grabbed all the external CSS files and combined them into one file.  I then ran the combined source into Stoyan’s tool.  Here is a look at what I found.

http://spreadsheets.google.com/pub?key=t5Zd4H3AD1MMa9agRNJhJsw&output=html

Stoyan’s tool doesn’t give me values for if it data was gzipped so I’m just looking at the savings for the raw version.  As the chart shows the savings were between 10 and 18% which once gzipped might have a return as high as 9%.  This is just a guess, but whatever the case it is still significant amount of savings for such a simple change.  I recommend Amazon integrate using YUI Compressor for CSS compression in the same manner as I suggested for JavaScript.

One thing to note is I ignored inline scripts and styles.  I’m assuming that a good portion of the inline code is generated dynamically, so integrating YUI Compressor to compress this additional code might not be a simple change.  I hope that Amazon will looks into the possibility, for the additional development to compress inline styles and scripts, may be of financial value.