SlideShare una empresa de Scribd logo
1 de 78
Descargar para leer sin conexión
HOW EDMUNDS GOT IN THE FAST LANE
    80% Reduction in Page Load Time in   Simple Steps




                                              by Ismail Elshareef

Sunday, January 16, 2011
The Results




  onLoad: 8.4s ➙ 1.9s      (≈ 80% reduction)


  Page Views: 20%
  Bounce Rate: 4%
  Ad Impression Variance: 3%

Sunday, January 16, 2011
Edmunds, Inc.

               Online since 1995
                Properties:
                200M+ page views/month
                Revenue = Ads + Leads




Sunday, January 16, 2011
Memory Lane




Sunday, January 16, 2011
Edmunds.com Legacy Site




Sunday, January 16, 2011
Meanwhile ...


                                                   “95% of Performance
                                                  is Frontend” - Steve Souders




                                        “Performance Matters!” - Organizations
                             +100ms in response time ➡ -1% in sales

                           -30% in file size ➡ +30% in requests

Sunday, January 16, 2011
The Real Picture!




Sunday, January 16, 2011
Site Condition

                           - Too Many Requests (150+)

                           - Many Blocking Requests (20+)

                           - Perceived Slowness (onLoad in 8.4s)

                           - No Caching

                           - 1 Domains Serves All Requests

                           - External Factors (ads, videos, ..etc)

                           - Dependency on DOM Events

Sunday, January 16, 2011
We Started Tinkering ...




Sunday, January 16, 2011
Quick Wins: CACHING!


      - Solution
              Added Expires Header + Removed Etags


      - Result:
              34% reduction in bandwidth
              = 34TB annual savings
              = FREE video streaming for 2 years
              = Faster pages when cache is primed


Sunday, January 16, 2011
... but Legacy was complicated.




Sunday, January 16, 2011
2008: A Vision Was Born




Sunday, January 16, 2011
Redesign Objectives


                           PERFORMANCE
                           (Faster Page Loads .. onLoad =< 1.5s)


                           RICHER CONTENT
                           (Flash, video, slicker UXD, ...etc)


                           BETTER REVENUE
                           (Positive impact on ad impressions)




Sunday, January 16, 2011
The Mindset



                                FASTER PAGES



                           POSITIVE USER EXPERIENCE



                               HIGHER REVENUE

Sunday, January 16, 2011
The Challenge


                 HTTP Requests                Page Performance




                                    User

                                 Experience




Sunday, January 16, 2011
Types of HTTP Requests




                             1) Our Own Requests
                               (files served from our own domains)




                             2) 3rd-party Requests
                                (files served from other domains)




Sunday, January 16, 2011
3rd-party      Requests


                                  Exist in Two Forms

                           JavaScript                 iFrame

                   Cons:                        Cons:
                     - Access to DOM              - Fixed width/height
                     - document.write
                                                Pros:
                   Pros:                           - Easy to lazy-load
                     - Richer Content              - Sandboxed Code




Sunday, January 16, 2011
3rd-party   Requests


                               Roles on Our Sites

                             JavaScript                  iFrame

                           Analytics
                            A/B Testing
                                             Ads
                           Video             Widgets
                             Monitoring
                                   Surveys




Sunday, January 16, 2011
Our Challenge

           3rd-party Requests
             HTTP Requests              Page Performance




                                User

                           Experience




Sunday, January 16, 2011
3rd-party          iFrame   3rd-party   JavaScript



                       Agreeable         Problematic

Sunday, January 16, 2011
Mission: Control 3rd

                Attempt #1:
                Override document.write()function


                Example:
                var buffer = [];
                document.write = function(st) {
                   buffer.push(st);
                }
                // when a specific event occurs ...
                var s = buffer.join(‘’);
                document.getElementById(‘destination’).innerHTML = s;


                Problem:
                Didn’t work with daisy-chained document.write calls


Sunday, January 16, 2011
Mission: Control 3rd


               Attempt #2:
               Load in iFrame and Copy on Load (iFrame’n’Copy)



               Example:
               var jsAd = “http://ad.doubleclick.net/adj/....”;
               iFrameObj.src = “http://www.edmunds.com/adSub.html?”+jsAd;

               // Option 1: listen to iFrame onLoad event
               // Option 2: iFrame page calls parent when done


                Problem:
                Buggy in IE 7 + Hard to Maintain


Sunday, January 16, 2011
Our New Found Creed




        You Can Not Control Everything
Sunday, January 16, 2011
Edmunds vs.             3rd-party           Requests


                                                                  %"#$



                    %"#$
                                                   &'()*'+,$-./).+0+$
                                                   12'345206$-./).+0+$
                                       !"#$



                                                                            !"#$




                           New Sites                                Old Sites

      &'()*'+,$-./).+0+$
      12'345206$-./).+0+$
Sunday, January 16, 2011
Our New Found Creed




        Make What You Control FAST
Sunday, January 16, 2011
JavaScript Loader




Sunday, January 16, 2011
How Does it Work?




Sunday, January 16, 2011
How Does it Work?
  ❶        Quick page is
  served to user




Sunday, January 16, 2011
How Does it Work?
                                      2       1
  ❶        Quick page is
                                              3
  served to user

                                          4

 ❷        Page components
 register themselves
 with the page
                                  5




                                                  6

                              7


Sunday, January 16, 2011
How Does it Work?
                                      2       1
  ❶        Quick page is
                                              3
  served to user

                                          4

 ❷        Page components
 register themselves
 with the page
                                  5



 ❸        Registered
 components rendered                              6


 in parallel                  7


Sunday, January 16, 2011
Component 1
                                                                  Component 2                Component 3


  Registration
                                                                         Component 4

                                                                         Component 5

      Process

                                                                       Component 6
                   I need YUI’s Carousel module. Here’s my
                         code, render me right away!




                 I need flash.js and here’s my code. I need to
                           be rendered right away
                                                                         Component 7




                   I need YUI’s Carousel module and here’s
                  my code. I am below the fold so I can wait    Component 8



Sunday, January 16, 2011
Component 1
                                                                  Component 2                Component 3


  Registration
                                                                         Component 4

                                                                         Component 5

      Process

                           Declare Dependencies
                                                                       Component 6
                   I need YUI’s Carousel module. Here’s my
                         code, render me right away!




                 I need flash.js and here’s my code. I need to
                           be rendered right away
                                                                         Component 7




                   I need YUI’s Carousel module and here’s
                  my code. I am below the fold so I can wait    Component 8



Sunday, January 16, 2011
Component 1
                                                                  Component 2                Component 3


  Registration
                                                                         Component 4

                                                                         Component 5

      Process

                           Declare Dependencies
                                                                       Component 6
                   I need YUI’s Carousel module. Here’s my
                         code, render me right away!




                           Submit Functionality

                 I need flash.js and here’s my code. I need to
                           be rendered right away
                                                                         Component 7




                   I need YUI’s Carousel module and here’s
                  my code. I am below the fold so I can wait    Component 8



Sunday, January 16, 2011
Component 1
                                                                  Component 2                Component 3


  Registration
                                                                         Component 4

                                                                         Component 5

      Process

                           Declare Dependencies
                                                                       Component 6
                   I need YUI’s Carousel module. Here’s my
                         code, render me right away!




                           Submit Functionality

                 I need flash.js and here’s my code. I need to
                           be rendered right away
                                                                         Component 7


                           Set Priority

                   I need YUI’s Carousel module and here’s
                  my code. I am below the fold so I can wait    Component 8



Sunday, January 16, 2011
Registration
                                                                                                      Process


                Declare Dependencies
                PAGESETUP.files.push('file1.js');
                PAGESETUP.files.push('file2.js');
                PAGESETUP.files.push('file7.js');


                  Submit functionality
                PAGESETUP.addControl(function() {
                           // ....
                           // Anything from rendering a component to making an AJAX call. All goes here.
                           // ...
                }, 'high');
                              Set Priority

Sunday, January 16, 2011
Process
  function load() {
      // .....
      // .....                                                        Dependencies
      var parent = arguments.callee;
      if (unique_files.length) {
           var file = unique_files.shift();
           var js = document.createElement('script');
           js.type = 'text/javascript';
           js.src = file;

                  if (!FF || FF >= 2) {
                      js.onreadystatechange = function() {
                         if (this.readyState == 'complete' ||
                             this.readyState == 'loaded' ||
                             this.status == 304 ||
                             this.status == 404)
                         {
                            parent();
                         }
                      };
                  } else {
                     parent();
                  }
                  document.getElementsByTagName('head')[0].appendChild(js);
                  if (PAGESETUP.execControls) {
                       PAGESETUP.execControls();
                  }
         }
  }


Sunday, January 16, 2011
Get a reference to
                                  the function                             Process
  function load() {
      // .....
      // .....                                                        Dependencies
      var parent = arguments.callee;
      if (unique_files.length) {
           var file = unique_files.shift();
           var js = document.createElement('script');
           js.type = 'text/javascript';
           js.src = file;

                  if (!FF || FF >= 2) {
                      js.onreadystatechange = function() {
                         if (this.readyState == 'complete' ||
                             this.readyState == 'loaded' ||
                             this.status == 304 ||
                             this.status == 404)
                         {
                            parent();
                         }
                      };
                  } else {
                     parent();
                  }
                  document.getElementsByTagName('head')[0].appendChild(js);
                  if (PAGESETUP.execControls) {
                       PAGESETUP.execControls();
                  }
         }
  }


Sunday, January 16, 2011
Get a reference to
                                  the function                             Process
  function load() {
      // .....
      // .....                                                        Dependencies
      var parent = arguments.callee;
      if (unique_files.length) {
           var file = unique_files.shift();
           var js = document.createElement('script');              Process unique
           js.type = 'text/javascript';
           js.src = file;                                           dependencies
                  if (!FF || FF >= 2) {
                      js.onreadystatechange = function() {
                         if (this.readyState == 'complete' ||
                             this.readyState == 'loaded' ||
                             this.status == 304 ||
                             this.status == 404)
                         {
                            parent();
                         }
                      };
                  } else {
                     parent();
                  }
                  document.getElementsByTagName('head')[0].appendChild(js);
                  if (PAGESETUP.execControls) {
                       PAGESETUP.execControls();
                  }
         }
  }


Sunday, January 16, 2011
Get a reference to
                                 the function                          Process
  function load() {
      // .....
      // .....                                                    Dependencies
      var parent = arguments.callee;
      if (unique_files.length) {
           var file = unique_files.shift();
           var js = document.createElement('script');          Process unique
           js.type = 'text/javascript';
           js.src = file;                                       dependencies
                  if (!FF || FF >= 2) {
                      js.onreadystatechange = function() {
                         if (this.readyState == 'complete' ||
                                                                       For all browsers but
                             this.readyState == 'loaded' ||          FireFox < 4, Download
                             this.status == 304 ||
                             this.status == 404)                    dependencies serially ...
                         {
                            parent();
                         }
                      };
                  } else {
                     parent();
                  }
                  document.getElementsByTagName('head')[0].appendChild(js);
                  if (PAGESETUP.execControls) {
                       PAGESETUP.execControls();
                  }
         }
  }


Sunday, January 16, 2011
Get a reference to
                                 the function                          Process
  function load() {
      // .....
      // .....                                                    Dependencies
      var parent = arguments.callee;
      if (unique_files.length) {
           var file = unique_files.shift();
           var js = document.createElement('script');          Process unique
           js.type = 'text/javascript';
           js.src = file;                                       dependencies
                  if (!FF || FF >= 2) {
                      js.onreadystatechange = function() {
                         if (this.readyState == 'complete' ||
                                                                       For all browsers but
                             this.readyState == 'loaded' ||          FireFox < 4, Download
                             this.status == 304 ||
                             this.status == 404)                    dependencies serially ...
                         {
                            parent();
                         }
                      };                Otherwise, download in
                  } else {
                                                   parallel
                     parent();
                  }
                  document.getElementsByTagName('head')[0].appendChild(js);
                  if (PAGESETUP.execControls) {
                       PAGESETUP.execControls();
                  }
         }
  }


Sunday, January 16, 2011
Get a reference to
                                 the function                          Process
  function load() {
      // .....
      // .....                                                    Dependencies
      var parent = arguments.callee;
      if (unique_files.length) {
           var file = unique_files.shift();
           var js = document.createElement('script');          Process unique
           js.type = 'text/javascript';
           js.src = file;                                       dependencies
                  if (!FF || FF >= 2) {
                      js.onreadystatechange = function() {
                         if (this.readyState == 'complete' ||
                                                                       For all browsers but
                             this.readyState == 'loaded' ||          FireFox < 4, Download
                             this.status == 304 ||
                             this.status == 404)                    dependencies serially ...
                         {
                            parent();
                         }
                      };                Otherwise, download in
                  } else {
                                                   parallel
                     parent();
                  }
                  document.getElementsByTagName('head')[0].appendChild(js);
                  if (PAGESETUP.execControls) {
                       PAGESETUP.execControls();
                  }
         }
                                                      When done downloading,
  }                                                render registered components!

Sunday, January 16, 2011
Rendering Components
                           execControls: function(start) {
                                // Get a timestamp here to indicate the start of the process
                                // Merge all the queues into one! high->normal->low
                                var merged = this.merged;

                                // Go through the merged array and execute the chunks!
                                setTimeout(function() {
                                       // Get a chunk from the top of the array
                                       var item = merged.shift();
                                       if(item){
                                           // Execute the chunk!
                                          item.call();
                                       }
                                       if (merged.length > 0) {
                                              // Wait 25 ms and then get the next chunk
                                           setTimeout(arguments.callee, 25);
                                       } else {
                                               // Otherwise, get a timestamp to indicate the end of the process
                                    }
                                }, 0);
                           },
Sunday, January 16, 2011
Rendering Components
                           execControls: function(start) {
                                // Get a timestamp here to indicate the start of the process
                                // Merge all the queues into one! high->normal->low
                                var merged = this.merged;

                                // Go through the merged array and execute the chunks!
                                setTimeout(function() {
                                       // Get a chunk from the top of the array
   Combine functions
                                       var item = merged.shift();
          submitted by                 if(item){
        components in                      // Execute the chunk!
                                          item.call();
  priority order (high
                                       }
    -> normal -> low)                  if (merged.length > 0) {
                                              // Wait 25 ms and then get the next chunk
                                           setTimeout(arguments.callee, 25);
                                       } else {
                                               // Otherwise, get a timestamp to indicate the end of the process
                                    }
                                }, 0);
                           },
Sunday, January 16, 2011
Rendering Components
                           execControls: function(start) {
                                // Get a timestamp here to indicate the start of the process
                                // Merge all the queues into one! high->normal->low
                                var merged = this.merged;

                                // Go through the merged array and execute the chunks!
                                setTimeout(function() {
                                       // Get a chunk from the top of the array
   Combine functions
                                       var item = merged.shift();
          submitted by                 if(item){
        components in                     // Execute the chunk! Process one function at
                                          item.call();                 a time ...
  priority order (high
                                        }
    -> normal -> low)                  if (merged.length > 0) {
                                              // Wait 25 ms and then get the next chunk
                                           setTimeout(arguments.callee, 25);
                                       } else {
                                               // Otherwise, get a timestamp to indicate the end of the process
                                    }
                                }, 0);
                           },
Sunday, January 16, 2011
Rendering Components
                           execControls: function(start) {
                                // Get a timestamp here to indicate the start of the process
                                // Merge all the queues into one! high->normal->low
                                var merged = this.merged;

                                // Go through the merged array and execute the chunks!
                                setTimeout(function() {
                                       // Get a chunk from the top of the array
   Combine functions
                                       var item = merged.shift();
          submitted by                 if(item){
        components in                     // Execute the chunk! Process one function at
                                          item.call();                 a time ...
  priority order (high
                                        }
    -> normal -> low)                  if (merged.length > 0) {
                                              // Wait 25 ms and then get the next chunk
                                           setTimeout(arguments.callee, 25);
                                       } else {
                                               // Otherwise, get a timestamp to indicate the end of the process
                                    }                                                               every 25 ms!
                                }, 0);
                           },
Sunday, January 16, 2011
Our New Found Creed




        Treat Everything Else as a Black Box
Sunday, January 16, 2011
3rd-party          iFrame   3rd-party   JavaScript



                       Agreeable         Problematic

Sunday, January 16, 2011
3rd-party          iFrame   3rd-party   JavaScript



                       Agreeable         Problematic

Sunday, January 16, 2011
3rd-party                 Handling Logic

                                                    Component                    Placeholder Markup
                                                    on a page

                                                                       YES



                                      NO            3rd-party?


                           Process through
                              JS Loader

                                                                                             JavaScript
                                                                                iFrame




                                                         Render before </html> in a       Register as "3rd-
                             Remove original call
                                                              hidden <div>                party" component


                             Relocate generated
                                markup into
                                 placeholder

Sunday, January 16, 2011
Rendering           3rd-party              JavaScript:
                                                                    Part 1 - Register the Call




      // Add the placeholder
      <div id="js-ad1"></div>
      <script type="text/javascript">
      (function() {
          // Construct the ad URL
          var ad = new EDMUNDS.AdUnit();
          // Inform the PAGESETUP object of what’s going on
          PAGESETUP.thirdpartyids.push('js-ad1');
          PAGESETUP.thirdpartydetails['js-ad1'] = {};
          PAGESETUP.thirdpartydetails['js-ad1']['type'] = 'ad';
          PAGESETUP.thirdpartydetails['js-ad1']['src'] = ad.getAdUrl();
      })();
      </script>




Sunday, January 16, 2011
Rendering          3rd-party              JavaScript:
                                                                    Part 1 - Register the Call




                           Placeholder Markup

      // Add the placeholder
      <div id="js-ad1"></div>
      <script type="text/javascript">
      (function() {
          // Construct the ad URL
          var ad = new EDMUNDS.AdUnit();
          // Inform the PAGESETUP object of what’s going on
          PAGESETUP.thirdpartyids.push('js-ad1');
          PAGESETUP.thirdpartydetails['js-ad1'] = {};
          PAGESETUP.thirdpartydetails['js-ad1']['type'] = 'ad';
          PAGESETUP.thirdpartydetails['js-ad1']['src'] = ad.getAdUrl();
      })();
      </script>




Sunday, January 16, 2011
Rendering          3rd-party            JavaScript:
                                                                    Part 1 - Register the Call




                           Placeholder Markup

      // Add the placeholder
      <div id="js-ad1"></div>
                                              Register details about
      <script type="text/javascript">                the JavaScript Request
      (function() {
          // Construct the ad URL
          var ad = new EDMUNDS.AdUnit();
          // Inform the PAGESETUP object of what’s going on
          PAGESETUP.thirdpartyids.push('js-ad1');
          PAGESETUP.thirdpartydetails['js-ad1'] = {};
          PAGESETUP.thirdpartydetails['js-ad1']['type'] = 'ad';
          PAGESETUP.thirdpartydetails['js-ad1']['src'] = ad.getAdUrl();
      })();
      </script>




Sunday, January 16, 2011
Rendering        3rd-party           JavaScript:
                                                                  Part 1 - Register the Call




                           Placeholder Markup

      // Add the placeholder
      <div id="js-ad1"></div>
                                             Register details about
      <script type="text/javascript">                the JavaScript Request
      (function() {
          // Construct the ad URL
          var ad = new EDMUNDS.AdUnit();
          // Inform the PAGESETUP object of what’s going on
                                                               Register: placeholder   ID
          PAGESETUP.thirdpartyids.push('js-ad1');
          PAGESETUP.thirdpartydetails['js-ad1'] = {};
          PAGESETUP.thirdpartydetails['js-ad1']['type'] = 'ad';
          PAGESETUP.thirdpartydetails['js-ad1']['src'] = ad.getAdUrl();
      })();
      </script>




Sunday, January 16, 2011
Rendering        3rd-party           JavaScript:
                                                                  Part 1 - Register the Call




                           Placeholder Markup

      // Add the placeholder
      <div id="js-ad1"></div>
                                             Register details about
      <script type="text/javascript">                the JavaScript Request
      (function() {
          // Construct the ad URL
          var ad = new EDMUNDS.AdUnit();
          // Inform the PAGESETUP object of what’s going on
                                                               Register: placeholder   ID
          PAGESETUP.thirdpartyids.push('js-ad1');
          PAGESETUP.thirdpartydetails['js-ad1'] = {};
          PAGESETUP.thirdpartydetails['js-ad1']['type'] = 'ad';
          PAGESETUP.thirdpartydetails['js-ad1']['src'] = ad.getAdUrl();
      })();
      </script>

                                                            Register: JavaScript Call


Sunday, January 16, 2011
Rendering              3rd-party           JavaScript:
                                                    Part 2 - Render at the bottom of the page




   <script type="text/javascript">
   if (PAGESETUP.thirdpartyids.length > 0) {
     (function() {
       var id = PAGESETUP.thirdpartyids.shift();
       var file = PAGESETUP.thirdpartydetails[id].src;
       if (file) {
         document.write('<div id="'+id+'-cache'+'" style="display:none;">
                         <div id="'+id+'-root'+'">');

             document.write('<script type="text/javascript" src="'+file+'" ></script>');
        }
      })();
   }
   </script>
   <script type="text/javascript">document.write('</div></div>');</script>




Sunday, January 16, 2011
Rendering              3rd-party           JavaScript:
                                                    Part 2 - Render at the bottom of the page




                                     Get the registered ID


   <script type="text/javascript">
   if (PAGESETUP.thirdpartyids.length > 0) {
     (function() {
       var id = PAGESETUP.thirdpartyids.shift();
       var file = PAGESETUP.thirdpartydetails[id].src;
       if (file) {
         document.write('<div id="'+id+'-cache'+'" style="display:none;">
                         <div id="'+id+'-root'+'">');

             document.write('<script type="text/javascript" src="'+file+'" ></script>');
        }
      })();
   }
   </script>
   <script type="text/javascript">document.write('</div></div>');</script>




Sunday, January 16, 2011
Rendering              3rd-party           JavaScript:
                                                    Part 2 - Render at the bottom of the page




                                     Get the registered ID


   <script type="text/javascript">                                  Get the registered
   if (PAGESETUP.thirdpartyids.length > 0) {
     (function() {                                                    JavaScript Call
       var id = PAGESETUP.thirdpartyids.shift();
       var file = PAGESETUP.thirdpartydetails[id].src;
       if (file) {
         document.write('<div id="'+id+'-cache'+'" style="display:none;">
                         <div id="'+id+'-root'+'">');

             document.write('<script type="text/javascript" src="'+file+'" ></script>');
        }
      })();
   }
   </script>
   <script type="text/javascript">document.write('</div></div>');</script>




Sunday, January 16, 2011
Rendering              3rd-party           JavaScript:
                                                    Part 2 - Render at the bottom of the page




                                     Get the registered ID


   <script type="text/javascript">                                  Get the registered
   if (PAGESETUP.thirdpartyids.length > 0) {
     (function() {                                                    JavaScript Call
       var id = PAGESETUP.thirdpartyids.shift();
       var file = PAGESETUP.thirdpartydetails[id].src;
       if (file) {
         document.write('<div id="'+id+'-cache'+'" style="display:none;">
                         <div id="'+id+'-root'+'">');

             document.write('<script type="text/javascript" src="'+file+'" ></script>');
        }
      })();                                                              Wrap the call in a
   }
   </script>                                                              hidden       DIV
   <script type="text/javascript">document.write('</div></div>');</script>




Sunday, January 16, 2011
Rendering              3rd-party           JavaScript:
                                                    Part 2 - Render at the bottom of the page




                                     Get the registered ID


   <script type="text/javascript">                                  Get the registered
   if (PAGESETUP.thirdpartyids.length > 0) {
     (function() {                                                    JavaScript Call
       var id = PAGESETUP.thirdpartyids.shift();
       var file = PAGESETUP.thirdpartydetails[id].src;
       if (file) {
         document.write('<div id="'+id+'-cache'+'" style="display:none;">
                         <div id="'+id+'-root'+'">');

             document.write('<script type="text/javascript" src="'+file+'" ></script>');
        }
      })();                                                              Wrap the call in a
   }
   </script>                                                              hidden       DIV
   <script type="text/javascript">document.write('</div></div>');</script>


                                                                      Close the DIV

Sunday, January 16, 2011
In Summary


        You Can Not Control Everything
                   It’s OK, it really is :-)


        Make What You Control FAST
                   No DOM Event dependency
                   Render components as soon as page is parsed
                   Render components in priority


        Treat Everything Else as a Black Box
                   Process separately
                   Delay inclusion as much as possible
                   It’s about “mitigation” not “elimination”
Sunday, January 16, 2011
Inside Line Results




Sunday, January 16, 2011
Inside Line Redesign Objectives


                           PERFORMANCE
                           (onLoad ≈1.5)


                           RICHER CONTENT
                           (Large Flash content serving photos and videos)


                           BETTER REVENUE
                           (Ad impression variance reduced by 3%)




Sunday, January 16, 2011
Another Vision Was Born




Sunday, January 16, 2011
Legacy   Redesign




Sunday, January 16, 2011
Edmunds Legacy                Edmunds Redesign




                     8.4 seconds          1.9 seconds




                                   ≈ 80% Reduction in
                                       Load Time!
Sunday, January 16, 2011
The Results




                           20%
                           Total Page Views Per Session


Sunday, January 16, 2011
The Results




                           3%
                           Ad Impression Variance


Sunday, January 16, 2011
The Results




                           4%
                           Bounce Rate


Sunday, January 16, 2011
In The Works ...




Sunday, January 16, 2011
JavaScript Loader 2.0


                           EVEN FASTER!
                           Load in parallel and execute on-demand


                           COMPONENT METRICS
                           (When it’s loaded, how long it took, ..etc)


                           MANY ENHANCEMENTS :)


Sunday, January 16, 2011
Soon ...




                           http://www.github.com/edmunds




Sunday, January 16, 2011
What About The Future?




Sunday, January 16, 2011
 You Can Not Control Everything
       Make What You Control FAST
       Treat Everything Else as a Black Box




                           How Will It Apply To ...
Sunday, January 16, 2011
Edmunds Mobile




Sunday, January 16, 2011
Edmunds Platform




Sunday, January 16, 2011
Edmunds Platform

           Plug-n-Play

                             Widgets
                                                 Syndication
       Managed



                                                                                      Products

        raw data           Content                               ®
                                                                                            Standalone

    packaged data                                                                   Tools
                                                                      Open Source               Plugins


                                                                                     Code Snippets
                 Cars

                                     Mobile UI
             Handheld


                                 Wired UI
                                                           API


                             Backend Systems


Sunday, January 16, 2011
Edmunds Platform

                           3rd-party Consumer



                                       ®




                           3rd-party Provider




Sunday, January 16, 2011
Let’s Continue the Conversation
                                                                     @codeish   http://tech.edmunds.com




Photo Credits:
All Car Photos are from www.insideline.com
http://www.cantronicsglobal.com/images/MissionVision.jpg
http://www.kewlwallpapers.com/images/wallpapers/John_C-680196.jpeg
Sunday, January 16, 2011

Más contenido relacionado

Similar a How Edmunds Got in the Fast Lane: 80% Reduction in Page Load Time in 3 Simple Steps

McAnerin technical seo
McAnerin   technical seoMcAnerin   technical seo
McAnerin technical seoIan McAnerin
 
Errors and handling them. YOW nights Sydney 2011
Errors and handling them. YOW nights Sydney 2011Errors and handling them. YOW nights Sydney 2011
Errors and handling them. YOW nights Sydney 2011Michael Neale
 
Error Handling Done Differently
Error Handling Done DifferentlyError Handling Done Differently
Error Handling Done DifferentlyCloudBees
 
HTML XHTML HTML5
HTML XHTML HTML5HTML XHTML HTML5
HTML XHTML HTML5timstone
 
How to use Mobile Applications to extend your brand
How to use Mobile Applications to extend your brandHow to use Mobile Applications to extend your brand
How to use Mobile Applications to extend your brandAxway Appcelerator
 
Debugging tips in YUI 3
Debugging tips in YUI 3Debugging tips in YUI 3
Debugging tips in YUI 3Luke Smith
 

Similar a How Edmunds Got in the Fast Lane: 80% Reduction in Page Load Time in 3 Simple Steps (7)

McAnerin technical seo
McAnerin   technical seoMcAnerin   technical seo
McAnerin technical seo
 
Errors and handling them. YOW nights Sydney 2011
Errors and handling them. YOW nights Sydney 2011Errors and handling them. YOW nights Sydney 2011
Errors and handling them. YOW nights Sydney 2011
 
Error Handling Done Differently
Error Handling Done DifferentlyError Handling Done Differently
Error Handling Done Differently
 
HTML XHTML HTML5
HTML XHTML HTML5HTML XHTML HTML5
HTML XHTML HTML5
 
Tim stone.html5.rjug.20110316
Tim stone.html5.rjug.20110316Tim stone.html5.rjug.20110316
Tim stone.html5.rjug.20110316
 
How to use Mobile Applications to extend your brand
How to use Mobile Applications to extend your brandHow to use Mobile Applications to extend your brand
How to use Mobile Applications to extend your brand
 
Debugging tips in YUI 3
Debugging tips in YUI 3Debugging tips in YUI 3
Debugging tips in YUI 3
 

Último

SALESFORCE EDUCATION CLOUD | FEXLE SERVICES
SALESFORCE EDUCATION CLOUD | FEXLE SERVICESSALESFORCE EDUCATION CLOUD | FEXLE SERVICES
SALESFORCE EDUCATION CLOUD | FEXLE SERVICESmohitsingh558521
 
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024BookNet Canada
 
unit 4 immunoblotting technique complete.pptx
unit 4 immunoblotting technique complete.pptxunit 4 immunoblotting technique complete.pptx
unit 4 immunoblotting technique complete.pptxBkGupta21
 
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024BookNet Canada
 
Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 365Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 3652toLead Limited
 
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptxUse of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptxLoriGlavin3
 
Unleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubUnleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubKalema Edgar
 
TeamStation AI System Report LATAM IT Salaries 2024
TeamStation AI System Report LATAM IT Salaries 2024TeamStation AI System Report LATAM IT Salaries 2024
TeamStation AI System Report LATAM IT Salaries 2024Lonnie McRorey
 
"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr Bagan"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr BaganFwdays
 
DSPy a system for AI to Write Prompts and Do Fine Tuning
DSPy a system for AI to Write Prompts and Do Fine TuningDSPy a system for AI to Write Prompts and Do Fine Tuning
DSPy a system for AI to Write Prompts and Do Fine TuningLars Bell
 
Gen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdfGen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdfAddepto
 
What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024Stephanie Beckett
 
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024BookNet Canada
 
Digital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptxDigital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptxLoriGlavin3
 
The Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and ConsThe Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and ConsPixlogix Infotech
 
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptxMerck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptxLoriGlavin3
 
How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.Curtis Poe
 
Dev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebDev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebUiPathCommunity
 
"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii SoldatenkoFwdays
 
Commit 2024 - Secret Management made easy
Commit 2024 - Secret Management made easyCommit 2024 - Secret Management made easy
Commit 2024 - Secret Management made easyAlfredo García Lavilla
 

Último (20)

SALESFORCE EDUCATION CLOUD | FEXLE SERVICES
SALESFORCE EDUCATION CLOUD | FEXLE SERVICESSALESFORCE EDUCATION CLOUD | FEXLE SERVICES
SALESFORCE EDUCATION CLOUD | FEXLE SERVICES
 
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
 
unit 4 immunoblotting technique complete.pptx
unit 4 immunoblotting technique complete.pptxunit 4 immunoblotting technique complete.pptx
unit 4 immunoblotting technique complete.pptx
 
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
 
Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 365Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 365
 
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptxUse of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
 
Unleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubUnleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding Club
 
TeamStation AI System Report LATAM IT Salaries 2024
TeamStation AI System Report LATAM IT Salaries 2024TeamStation AI System Report LATAM IT Salaries 2024
TeamStation AI System Report LATAM IT Salaries 2024
 
"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr Bagan"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr Bagan
 
DSPy a system for AI to Write Prompts and Do Fine Tuning
DSPy a system for AI to Write Prompts and Do Fine TuningDSPy a system for AI to Write Prompts and Do Fine Tuning
DSPy a system for AI to Write Prompts and Do Fine Tuning
 
Gen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdfGen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdf
 
What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024
 
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
 
Digital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptxDigital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptx
 
The Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and ConsThe Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and Cons
 
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptxMerck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptx
 
How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.
 
Dev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebDev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio Web
 
"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko
 
Commit 2024 - Secret Management made easy
Commit 2024 - Secret Management made easyCommit 2024 - Secret Management made easy
Commit 2024 - Secret Management made easy
 

How Edmunds Got in the Fast Lane: 80% Reduction in Page Load Time in 3 Simple Steps

  • 1. HOW EDMUNDS GOT IN THE FAST LANE 80% Reduction in Page Load Time in Simple Steps by Ismail Elshareef Sunday, January 16, 2011
  • 2. The Results onLoad: 8.4s ➙ 1.9s (≈ 80% reduction) Page Views: 20% Bounce Rate: 4% Ad Impression Variance: 3% Sunday, January 16, 2011
  • 3. Edmunds, Inc. Online since 1995 Properties: 200M+ page views/month Revenue = Ads + Leads Sunday, January 16, 2011
  • 6. Meanwhile ... “95% of Performance is Frontend” - Steve Souders “Performance Matters!” - Organizations +100ms in response time ➡ -1% in sales -30% in file size ➡ +30% in requests Sunday, January 16, 2011
  • 7. The Real Picture! Sunday, January 16, 2011
  • 8. Site Condition - Too Many Requests (150+) - Many Blocking Requests (20+) - Perceived Slowness (onLoad in 8.4s) - No Caching - 1 Domains Serves All Requests - External Factors (ads, videos, ..etc) - Dependency on DOM Events Sunday, January 16, 2011
  • 9. We Started Tinkering ... Sunday, January 16, 2011
  • 10. Quick Wins: CACHING! - Solution Added Expires Header + Removed Etags - Result: 34% reduction in bandwidth = 34TB annual savings = FREE video streaming for 2 years = Faster pages when cache is primed Sunday, January 16, 2011
  • 11. ... but Legacy was complicated. Sunday, January 16, 2011
  • 12. 2008: A Vision Was Born Sunday, January 16, 2011
  • 13. Redesign Objectives PERFORMANCE (Faster Page Loads .. onLoad =< 1.5s) RICHER CONTENT (Flash, video, slicker UXD, ...etc) BETTER REVENUE (Positive impact on ad impressions) Sunday, January 16, 2011
  • 14. The Mindset FASTER PAGES POSITIVE USER EXPERIENCE HIGHER REVENUE Sunday, January 16, 2011
  • 15. The Challenge HTTP Requests Page Performance User Experience Sunday, January 16, 2011
  • 16. Types of HTTP Requests 1) Our Own Requests (files served from our own domains) 2) 3rd-party Requests (files served from other domains) Sunday, January 16, 2011
  • 17. 3rd-party Requests Exist in Two Forms JavaScript iFrame Cons: Cons: - Access to DOM - Fixed width/height - document.write Pros: Pros: - Easy to lazy-load - Richer Content - Sandboxed Code Sunday, January 16, 2011
  • 18. 3rd-party Requests Roles on Our Sites JavaScript iFrame Analytics A/B Testing Ads Video Widgets Monitoring Surveys Sunday, January 16, 2011
  • 19. Our Challenge 3rd-party Requests HTTP Requests Page Performance User Experience Sunday, January 16, 2011
  • 20. 3rd-party iFrame 3rd-party JavaScript Agreeable Problematic Sunday, January 16, 2011
  • 21. Mission: Control 3rd Attempt #1: Override document.write()function Example: var buffer = []; document.write = function(st) { buffer.push(st); } // when a specific event occurs ... var s = buffer.join(‘’); document.getElementById(‘destination’).innerHTML = s; Problem: Didn’t work with daisy-chained document.write calls Sunday, January 16, 2011
  • 22. Mission: Control 3rd Attempt #2: Load in iFrame and Copy on Load (iFrame’n’Copy) Example: var jsAd = “http://ad.doubleclick.net/adj/....”; iFrameObj.src = “http://www.edmunds.com/adSub.html?”+jsAd; // Option 1: listen to iFrame onLoad event // Option 2: iFrame page calls parent when done Problem: Buggy in IE 7 + Hard to Maintain Sunday, January 16, 2011
  • 23. Our New Found Creed  You Can Not Control Everything Sunday, January 16, 2011
  • 24. Edmunds vs. 3rd-party Requests %"#$ %"#$ &'()*'+,$-./).+0+$ 12'345206$-./).+0+$ !"#$ !"#$ New Sites Old Sites &'()*'+,$-./).+0+$ 12'345206$-./).+0+$ Sunday, January 16, 2011
  • 25. Our New Found Creed  Make What You Control FAST Sunday, January 16, 2011
  • 27. How Does it Work? Sunday, January 16, 2011
  • 28. How Does it Work? ❶ Quick page is served to user Sunday, January 16, 2011
  • 29. How Does it Work? 2 1 ❶ Quick page is 3 served to user 4 ❷ Page components register themselves with the page 5 6 7 Sunday, January 16, 2011
  • 30. How Does it Work? 2 1 ❶ Quick page is 3 served to user 4 ❷ Page components register themselves with the page 5 ❸ Registered components rendered 6 in parallel 7 Sunday, January 16, 2011
  • 31. Component 1 Component 2 Component 3 Registration Component 4 Component 5 Process Component 6 I need YUI’s Carousel module. Here’s my code, render me right away! I need flash.js and here’s my code. I need to be rendered right away Component 7 I need YUI’s Carousel module and here’s my code. I am below the fold so I can wait Component 8 Sunday, January 16, 2011
  • 32. Component 1 Component 2 Component 3 Registration Component 4 Component 5 Process Declare Dependencies Component 6 I need YUI’s Carousel module. Here’s my code, render me right away! I need flash.js and here’s my code. I need to be rendered right away Component 7 I need YUI’s Carousel module and here’s my code. I am below the fold so I can wait Component 8 Sunday, January 16, 2011
  • 33. Component 1 Component 2 Component 3 Registration Component 4 Component 5 Process Declare Dependencies Component 6 I need YUI’s Carousel module. Here’s my code, render me right away! Submit Functionality I need flash.js and here’s my code. I need to be rendered right away Component 7 I need YUI’s Carousel module and here’s my code. I am below the fold so I can wait Component 8 Sunday, January 16, 2011
  • 34. Component 1 Component 2 Component 3 Registration Component 4 Component 5 Process Declare Dependencies Component 6 I need YUI’s Carousel module. Here’s my code, render me right away! Submit Functionality I need flash.js and here’s my code. I need to be rendered right away Component 7 Set Priority I need YUI’s Carousel module and here’s my code. I am below the fold so I can wait Component 8 Sunday, January 16, 2011
  • 35. Registration Process Declare Dependencies PAGESETUP.files.push('file1.js'); PAGESETUP.files.push('file2.js'); PAGESETUP.files.push('file7.js'); Submit functionality PAGESETUP.addControl(function() { // .... // Anything from rendering a component to making an AJAX call. All goes here. // ... }, 'high'); Set Priority Sunday, January 16, 2011
  • 36. Process function load() { // ..... // ..... Dependencies var parent = arguments.callee; if (unique_files.length) { var file = unique_files.shift(); var js = document.createElement('script'); js.type = 'text/javascript'; js.src = file; if (!FF || FF >= 2) { js.onreadystatechange = function() { if (this.readyState == 'complete' || this.readyState == 'loaded' || this.status == 304 || this.status == 404) { parent(); } }; } else { parent(); } document.getElementsByTagName('head')[0].appendChild(js); if (PAGESETUP.execControls) { PAGESETUP.execControls(); } } } Sunday, January 16, 2011
  • 37. Get a reference to the function Process function load() { // ..... // ..... Dependencies var parent = arguments.callee; if (unique_files.length) { var file = unique_files.shift(); var js = document.createElement('script'); js.type = 'text/javascript'; js.src = file; if (!FF || FF >= 2) { js.onreadystatechange = function() { if (this.readyState == 'complete' || this.readyState == 'loaded' || this.status == 304 || this.status == 404) { parent(); } }; } else { parent(); } document.getElementsByTagName('head')[0].appendChild(js); if (PAGESETUP.execControls) { PAGESETUP.execControls(); } } } Sunday, January 16, 2011
  • 38. Get a reference to the function Process function load() { // ..... // ..... Dependencies var parent = arguments.callee; if (unique_files.length) { var file = unique_files.shift(); var js = document.createElement('script'); Process unique js.type = 'text/javascript'; js.src = file; dependencies if (!FF || FF >= 2) { js.onreadystatechange = function() { if (this.readyState == 'complete' || this.readyState == 'loaded' || this.status == 304 || this.status == 404) { parent(); } }; } else { parent(); } document.getElementsByTagName('head')[0].appendChild(js); if (PAGESETUP.execControls) { PAGESETUP.execControls(); } } } Sunday, January 16, 2011
  • 39. Get a reference to the function Process function load() { // ..... // ..... Dependencies var parent = arguments.callee; if (unique_files.length) { var file = unique_files.shift(); var js = document.createElement('script'); Process unique js.type = 'text/javascript'; js.src = file; dependencies if (!FF || FF >= 2) { js.onreadystatechange = function() { if (this.readyState == 'complete' || For all browsers but this.readyState == 'loaded' || FireFox < 4, Download this.status == 304 || this.status == 404) dependencies serially ... { parent(); } }; } else { parent(); } document.getElementsByTagName('head')[0].appendChild(js); if (PAGESETUP.execControls) { PAGESETUP.execControls(); } } } Sunday, January 16, 2011
  • 40. Get a reference to the function Process function load() { // ..... // ..... Dependencies var parent = arguments.callee; if (unique_files.length) { var file = unique_files.shift(); var js = document.createElement('script'); Process unique js.type = 'text/javascript'; js.src = file; dependencies if (!FF || FF >= 2) { js.onreadystatechange = function() { if (this.readyState == 'complete' || For all browsers but this.readyState == 'loaded' || FireFox < 4, Download this.status == 304 || this.status == 404) dependencies serially ... { parent(); } }; Otherwise, download in } else { parallel parent(); } document.getElementsByTagName('head')[0].appendChild(js); if (PAGESETUP.execControls) { PAGESETUP.execControls(); } } } Sunday, January 16, 2011
  • 41. Get a reference to the function Process function load() { // ..... // ..... Dependencies var parent = arguments.callee; if (unique_files.length) { var file = unique_files.shift(); var js = document.createElement('script'); Process unique js.type = 'text/javascript'; js.src = file; dependencies if (!FF || FF >= 2) { js.onreadystatechange = function() { if (this.readyState == 'complete' || For all browsers but this.readyState == 'loaded' || FireFox < 4, Download this.status == 304 || this.status == 404) dependencies serially ... { parent(); } }; Otherwise, download in } else { parallel parent(); } document.getElementsByTagName('head')[0].appendChild(js); if (PAGESETUP.execControls) { PAGESETUP.execControls(); } } When done downloading, } render registered components! Sunday, January 16, 2011
  • 42. Rendering Components execControls: function(start) { // Get a timestamp here to indicate the start of the process // Merge all the queues into one! high->normal->low var merged = this.merged; // Go through the merged array and execute the chunks! setTimeout(function() { // Get a chunk from the top of the array var item = merged.shift(); if(item){ // Execute the chunk! item.call(); } if (merged.length > 0) { // Wait 25 ms and then get the next chunk setTimeout(arguments.callee, 25); } else { // Otherwise, get a timestamp to indicate the end of the process } }, 0); }, Sunday, January 16, 2011
  • 43. Rendering Components execControls: function(start) { // Get a timestamp here to indicate the start of the process // Merge all the queues into one! high->normal->low var merged = this.merged; // Go through the merged array and execute the chunks! setTimeout(function() { // Get a chunk from the top of the array Combine functions var item = merged.shift(); submitted by if(item){ components in // Execute the chunk! item.call(); priority order (high } -> normal -> low) if (merged.length > 0) { // Wait 25 ms and then get the next chunk setTimeout(arguments.callee, 25); } else { // Otherwise, get a timestamp to indicate the end of the process } }, 0); }, Sunday, January 16, 2011
  • 44. Rendering Components execControls: function(start) { // Get a timestamp here to indicate the start of the process // Merge all the queues into one! high->normal->low var merged = this.merged; // Go through the merged array and execute the chunks! setTimeout(function() { // Get a chunk from the top of the array Combine functions var item = merged.shift(); submitted by if(item){ components in // Execute the chunk! Process one function at item.call(); a time ... priority order (high } -> normal -> low) if (merged.length > 0) { // Wait 25 ms and then get the next chunk setTimeout(arguments.callee, 25); } else { // Otherwise, get a timestamp to indicate the end of the process } }, 0); }, Sunday, January 16, 2011
  • 45. Rendering Components execControls: function(start) { // Get a timestamp here to indicate the start of the process // Merge all the queues into one! high->normal->low var merged = this.merged; // Go through the merged array and execute the chunks! setTimeout(function() { // Get a chunk from the top of the array Combine functions var item = merged.shift(); submitted by if(item){ components in // Execute the chunk! Process one function at item.call(); a time ... priority order (high } -> normal -> low) if (merged.length > 0) { // Wait 25 ms and then get the next chunk setTimeout(arguments.callee, 25); } else { // Otherwise, get a timestamp to indicate the end of the process } every 25 ms! }, 0); }, Sunday, January 16, 2011
  • 46. Our New Found Creed  Treat Everything Else as a Black Box Sunday, January 16, 2011
  • 47. 3rd-party iFrame 3rd-party JavaScript Agreeable Problematic Sunday, January 16, 2011
  • 48. 3rd-party iFrame 3rd-party JavaScript Agreeable Problematic Sunday, January 16, 2011
  • 49. 3rd-party Handling Logic Component Placeholder Markup on a page YES NO 3rd-party? Process through JS Loader JavaScript iFrame Render before </html> in a Register as "3rd- Remove original call hidden <div> party" component Relocate generated markup into placeholder Sunday, January 16, 2011
  • 50. Rendering 3rd-party JavaScript: Part 1 - Register the Call // Add the placeholder <div id="js-ad1"></div> <script type="text/javascript"> (function() { // Construct the ad URL var ad = new EDMUNDS.AdUnit(); // Inform the PAGESETUP object of what’s going on PAGESETUP.thirdpartyids.push('js-ad1'); PAGESETUP.thirdpartydetails['js-ad1'] = {}; PAGESETUP.thirdpartydetails['js-ad1']['type'] = 'ad'; PAGESETUP.thirdpartydetails['js-ad1']['src'] = ad.getAdUrl(); })(); </script> Sunday, January 16, 2011
  • 51. Rendering 3rd-party JavaScript: Part 1 - Register the Call Placeholder Markup // Add the placeholder <div id="js-ad1"></div> <script type="text/javascript"> (function() { // Construct the ad URL var ad = new EDMUNDS.AdUnit(); // Inform the PAGESETUP object of what’s going on PAGESETUP.thirdpartyids.push('js-ad1'); PAGESETUP.thirdpartydetails['js-ad1'] = {}; PAGESETUP.thirdpartydetails['js-ad1']['type'] = 'ad'; PAGESETUP.thirdpartydetails['js-ad1']['src'] = ad.getAdUrl(); })(); </script> Sunday, January 16, 2011
  • 52. Rendering 3rd-party JavaScript: Part 1 - Register the Call Placeholder Markup // Add the placeholder <div id="js-ad1"></div> Register details about <script type="text/javascript"> the JavaScript Request (function() { // Construct the ad URL var ad = new EDMUNDS.AdUnit(); // Inform the PAGESETUP object of what’s going on PAGESETUP.thirdpartyids.push('js-ad1'); PAGESETUP.thirdpartydetails['js-ad1'] = {}; PAGESETUP.thirdpartydetails['js-ad1']['type'] = 'ad'; PAGESETUP.thirdpartydetails['js-ad1']['src'] = ad.getAdUrl(); })(); </script> Sunday, January 16, 2011
  • 53. Rendering 3rd-party JavaScript: Part 1 - Register the Call Placeholder Markup // Add the placeholder <div id="js-ad1"></div> Register details about <script type="text/javascript"> the JavaScript Request (function() { // Construct the ad URL var ad = new EDMUNDS.AdUnit(); // Inform the PAGESETUP object of what’s going on Register: placeholder ID PAGESETUP.thirdpartyids.push('js-ad1'); PAGESETUP.thirdpartydetails['js-ad1'] = {}; PAGESETUP.thirdpartydetails['js-ad1']['type'] = 'ad'; PAGESETUP.thirdpartydetails['js-ad1']['src'] = ad.getAdUrl(); })(); </script> Sunday, January 16, 2011
  • 54. Rendering 3rd-party JavaScript: Part 1 - Register the Call Placeholder Markup // Add the placeholder <div id="js-ad1"></div> Register details about <script type="text/javascript"> the JavaScript Request (function() { // Construct the ad URL var ad = new EDMUNDS.AdUnit(); // Inform the PAGESETUP object of what’s going on Register: placeholder ID PAGESETUP.thirdpartyids.push('js-ad1'); PAGESETUP.thirdpartydetails['js-ad1'] = {}; PAGESETUP.thirdpartydetails['js-ad1']['type'] = 'ad'; PAGESETUP.thirdpartydetails['js-ad1']['src'] = ad.getAdUrl(); })(); </script> Register: JavaScript Call Sunday, January 16, 2011
  • 55. Rendering 3rd-party JavaScript: Part 2 - Render at the bottom of the page <script type="text/javascript"> if (PAGESETUP.thirdpartyids.length > 0) { (function() { var id = PAGESETUP.thirdpartyids.shift(); var file = PAGESETUP.thirdpartydetails[id].src; if (file) { document.write('<div id="'+id+'-cache'+'" style="display:none;"> <div id="'+id+'-root'+'">'); document.write('<script type="text/javascript" src="'+file+'" ></script>'); } })(); } </script> <script type="text/javascript">document.write('</div></div>');</script> Sunday, January 16, 2011
  • 56. Rendering 3rd-party JavaScript: Part 2 - Render at the bottom of the page Get the registered ID <script type="text/javascript"> if (PAGESETUP.thirdpartyids.length > 0) { (function() { var id = PAGESETUP.thirdpartyids.shift(); var file = PAGESETUP.thirdpartydetails[id].src; if (file) { document.write('<div id="'+id+'-cache'+'" style="display:none;"> <div id="'+id+'-root'+'">'); document.write('<script type="text/javascript" src="'+file+'" ></script>'); } })(); } </script> <script type="text/javascript">document.write('</div></div>');</script> Sunday, January 16, 2011
  • 57. Rendering 3rd-party JavaScript: Part 2 - Render at the bottom of the page Get the registered ID <script type="text/javascript"> Get the registered if (PAGESETUP.thirdpartyids.length > 0) { (function() { JavaScript Call var id = PAGESETUP.thirdpartyids.shift(); var file = PAGESETUP.thirdpartydetails[id].src; if (file) { document.write('<div id="'+id+'-cache'+'" style="display:none;"> <div id="'+id+'-root'+'">'); document.write('<script type="text/javascript" src="'+file+'" ></script>'); } })(); } </script> <script type="text/javascript">document.write('</div></div>');</script> Sunday, January 16, 2011
  • 58. Rendering 3rd-party JavaScript: Part 2 - Render at the bottom of the page Get the registered ID <script type="text/javascript"> Get the registered if (PAGESETUP.thirdpartyids.length > 0) { (function() { JavaScript Call var id = PAGESETUP.thirdpartyids.shift(); var file = PAGESETUP.thirdpartydetails[id].src; if (file) { document.write('<div id="'+id+'-cache'+'" style="display:none;"> <div id="'+id+'-root'+'">'); document.write('<script type="text/javascript" src="'+file+'" ></script>'); } })(); Wrap the call in a } </script> hidden DIV <script type="text/javascript">document.write('</div></div>');</script> Sunday, January 16, 2011
  • 59. Rendering 3rd-party JavaScript: Part 2 - Render at the bottom of the page Get the registered ID <script type="text/javascript"> Get the registered if (PAGESETUP.thirdpartyids.length > 0) { (function() { JavaScript Call var id = PAGESETUP.thirdpartyids.shift(); var file = PAGESETUP.thirdpartydetails[id].src; if (file) { document.write('<div id="'+id+'-cache'+'" style="display:none;"> <div id="'+id+'-root'+'">'); document.write('<script type="text/javascript" src="'+file+'" ></script>'); } })(); Wrap the call in a } </script> hidden DIV <script type="text/javascript">document.write('</div></div>');</script> Close the DIV Sunday, January 16, 2011
  • 60. In Summary  You Can Not Control Everything It’s OK, it really is :-)  Make What You Control FAST No DOM Event dependency Render components as soon as page is parsed Render components in priority  Treat Everything Else as a Black Box Process separately Delay inclusion as much as possible It’s about “mitigation” not “elimination” Sunday, January 16, 2011
  • 61. Inside Line Results Sunday, January 16, 2011
  • 62. Inside Line Redesign Objectives PERFORMANCE (onLoad ≈1.5) RICHER CONTENT (Large Flash content serving photos and videos) BETTER REVENUE (Ad impression variance reduced by 3%) Sunday, January 16, 2011
  • 63. Another Vision Was Born Sunday, January 16, 2011
  • 64. Legacy Redesign Sunday, January 16, 2011
  • 65. Edmunds Legacy Edmunds Redesign 8.4 seconds 1.9 seconds ≈ 80% Reduction in Load Time! Sunday, January 16, 2011
  • 66. The Results 20% Total Page Views Per Session Sunday, January 16, 2011
  • 67. The Results 3% Ad Impression Variance Sunday, January 16, 2011
  • 68. The Results 4% Bounce Rate Sunday, January 16, 2011
  • 69. In The Works ... Sunday, January 16, 2011
  • 70. JavaScript Loader 2.0 EVEN FASTER! Load in parallel and execute on-demand COMPONENT METRICS (When it’s loaded, how long it took, ..etc) MANY ENHANCEMENTS :) Sunday, January 16, 2011
  • 71. Soon ... http://www.github.com/edmunds Sunday, January 16, 2011
  • 72. What About The Future? Sunday, January 16, 2011
  • 73.  You Can Not Control Everything  Make What You Control FAST  Treat Everything Else as a Black Box How Will It Apply To ... Sunday, January 16, 2011
  • 76. Edmunds Platform Plug-n-Play Widgets Syndication Managed Products raw data Content ® Standalone packaged data Tools Open Source Plugins Code Snippets Cars Mobile UI Handheld Wired UI API Backend Systems Sunday, January 16, 2011
  • 77. Edmunds Platform 3rd-party Consumer ® 3rd-party Provider Sunday, January 16, 2011
  • 78. Let’s Continue the Conversation @codeish http://tech.edmunds.com Photo Credits: All Car Photos are from www.insideline.com http://www.cantronicsglobal.com/images/MissionVision.jpg http://www.kewlwallpapers.com/images/wallpapers/John_C-680196.jpeg Sunday, January 16, 2011