Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Speed at Scale: Web Performance Tips and Tricks from the Trenches

Speed at Scale: Web Performance Tips and Tricks from the Trenches

Getting your site fast and keeping it fast can be a challenge at scale. Learn 15 tips and tricks that real, production sites use to get great scores on Lighthouse and improve core business metrics. Understand a spectrum of optimizations from latency optimization to JavaScript, preloading, prefetching, data-fetching, and more.

This talk is from Google I/O 2019: https://events.google.com/io/schedule/events/9888b5c4-33eb-42d7-97c8-4cef89f20b98

Addy Osmani

May 08, 2019

More Decks by Addy Osmani

Other Decks in Programming


  1. Speed at Scale WebPerf Tips & Tricks From The Trenches

    2019 Google I/O Katie Hempenius @katiehempenius Addy Osmani @addyosmani
  2. Proprietary + Confidential Source: Lorem ipsum dolor sit amet, consectetur

    adipiscing elit. Duis non erat sem Delightful user experiences can be found everywhere
  3. 40% of brands regress on web performance after 6 months.

    Internal Google study into Web Performance investments by large brands.
  4. Time < 2 second TTI KB KB Resources < 150KB

    JS Lighthouse 90+ Perf Score
  5. Catch issues before they ship “Checking the size of our

    builds ensures that we don’t miss potential performance issues. For example, this has caught dependencies being added unnecessarily. Since the PR was blocked, we were able to pinpoint the issue and fix it before it impacted our build." “By setting a budget for tracking, and reporting build size deltas, we've prevented changes that would have increased metrics like TTI, TTFMC and Time to First Tweet by over 100%.” Paul Armstrong, Twitter Jeff McRiffey, Walmart eCommerce
  6. Job checks and logs the size of all builds. PRs

    fail if size increases by > 1% (main bundles only) Issues are escalated to a performance engineer. > node bundle-check.js +4.4% - discoveryJS +0.0% - discoveryCSS +0.0% - coreJS -0.0% - checkoutJS +0.0% - checkoutCSS
  7. Build tracker comments on PRs with a detailed breakdown of

    changes. Build tracker logs the size of all production builds. Engineers use this info when reviewing PRs.
  8. 1. Add a budget.json file 2. Run Lighthouse 5.0+ from

    the CLI $ lighthouse https://example.com --budgetPath ./budget.json LightWallet bit.ly/lightwallet-docs
  9. [{ "resourceSizes": [ { "resourceType": "image", "budget": 250 }, {

    "resourceType": "script", "budget": 125 }, { "resourceType": "font", "budget": 30 }, { "resourceType": "document", "budget": 25 }, { "resourceType": "stylesheet", "budget": 5 }, { "resourceType": "third-party", "budget": 400 } ] }]; budget.json Budget types: • Resource Sizes • Resource Counts
  10. 750KB 3.5 MB JavaScript Other Time to Interactive of 3.9

    million mobile sites Median: 8.9 seconds Data source: HTTP Archive
  11. 4.1s Range: 3.2-5.1s 5.1s Range: 4.1-6.4s 6s Range: 4.8-7.5s 100

    KB JS 200 KB JS 300 KB JS 300 KB other 300 KB other 300 KB other bit.ly/perf-budget-calculator
  12. Am I a joke to you? Data Plan 5MB Images

    a page ~4.7MB images at P90 from HTTP Archive
  13. Lazy-load offscreen images Eager Loading Load images right away Lazy

    Loading Load (offscreen) images on-demand 2MB 1MB Potential Savings 3MB (P90) and 416KB (Median)
  14. <img src="images/unicorn.jpg"> <img data-src="images/unicorn.jpg" class="lazyload"> Solid Color 1KB 13KB Progressive

    image loading LQIP (Low-quality placeholder) Lazy-loading images with JS Common
  15. Optimizing Chrome.com Faster page load times (mobile) 20% Faster page

    load times (desktop - Windows) 26% chrome.com Credits: Vitaly Keyzla (Dev Lead, Infra) Wei-Hsin Chen (Senior Eng/Lead) Luis Villalba (Senior Eng/Lead) Felipe Angel (Eng) Cristian Perez (QA) Katia Pena (QA Lead) Luis Jimenez (Director QA) Neil Shankar (UI Designer) Melissa Castano (Senior UI Designer) Esteban Lobo Guerrero (UI/UX Designer) McKenzy Germain (Web Product Manager) Kevin Furuichi (Android/Chrome Program Manager) Adam Dunlavey (Senior Web Analyst)
  16. Lazy-Load Offscreen Images Fewer Image Bytes Loaded Upfront 46% Chrome.com

    lazy-loads offscreen images. They use an SVG placeholder with image dimensions to avoid reflow. Their custom JS lazy-loader leverages Intersection Observer.
  17. Lazy-load offscreen images Defer render until decode Before After Initial

    state Final state Or download if img.decode() unsupported
  18. Optimized CPU load, memory & network traffic Images 4.4MB Images

    1.2MB Full Page Load Initial Page State ...leading to faster start times for video previews. DOM Nodes 11,744 Memory 45MB DOM Nodes 650 Memory 8MB bit.ly/netflix-previews
  19. Introducing native image lazy-loading! <img loading=lazy> Eager Loading <img loading=eager>

    Lazy Loading <img loading=lazy> 2MB 1MB chrome://flags/#enable-lazy-image-loading
  20. Introducing native iframe lazy-loading! <iframe loading=lazy> Eager Loading <iframe loading=eager>

    Lazy Loading <iframe loading=lazy> 3MB iframe 0MB iframe chrome://flags/#enable-lazy-frame-loading
  21. <!-- Let's load this in-viewport image normally --> <img src="hero.jpg"

    alt=".." /> <!-- Let's lazy-load the rest of these images --> <img data-src="cat-1.jpg" alt=".." class="lazyload" loading="lazy" /> <img data-src="cat-2.jpg" alt=".." class="lazyload" loading="lazy" /> <script> if ('loading' in HTMLImageElement.prototype) { const images = document.querySelectorAll("img.lazyload"); images.forEach(img => { img.src = img.dataset.src; }); } else { // Dynamically load the lazysizes library let script = document.createElement("script"); script.src = "/lazysizes.min.js"; document.body.appendChild(script); } </script> Cross-browser Image lazy-loading Feature Detection JS library fallback
  22. 101 KB 299 KB vs. Images for desktop & tablet

    can be ~2-4x larger than the mobile version.
  23. By width By density <img src="cat.jpg" srcset="cat-1x.jpg 1x, cat-2x.jpg 2x,

    cat-3x.jpg 3x"> <img src="cat.jpg" srcset="cat-240.jpg 240w, cat-480.jpg 480w cat-960.jpg 960w> Responsive Images Width descriptor w ≈ px window.devicePixelRatio Defines the relationship between device pixels and CSS pixels for a particular device.
  24. Pixel density • Limits image pixel density to <= 2x

    • Exception: full-screen images where users can pinch zoom. 33% decrease in image size 1x 2x 3x Result: No distinguishable difference to the human eye. 4x
  25. Popular Image CDNs Akamai Image Manager | Cloudinary | Imgix

    | Thumbor (self-hosted) ...cat.jpg?q=50&w=300&h=200 Set quality level to 50 Resize to 300x200
  26. Image CDNs "We realized that maintaining an inhouse image optimization

    solution would be enough work for a dedicated team. By using an Image CDN, our engineers can focus on our core business while our users benefit from fast, good-looking images across all devices & networks." Tobias Baldauf, Trivago
  27. Image CDNs • Auto quality • Auto cropping • WebP

    • Color channel tuning Decrease in image size (KB) 80%
  28. 57% Patrick Hulce, thirdpartyweb.today. Data from across top 4M sites.

    57% JavaScript execution time on the web is third-party code
  29. Gareth Clubb Everyone wants ‘that tag’ on a page that

    will make the organisation money. It’s very important to get the right individuals in a room to educate, challenge and work together. Create a Performance Culture
  30. Third-party JS 2s 5s bit.ly/telegraph-3p Defer <script> <script defer> bit.ly/telegraph-3p

    “Our biggest improvement was deferring all JS, including our own” “...this didn’t skew analytics or advertising. Lighthouse Perf +47” Other optimizations 1MB reduction 1st & 3p JS 4s faster “first ad” loading 6s faster Time-to-Interactive
  31. When load times decreased 71%, bounce rates decreased 31% Faster

    Page Load Times 78% Decrease in Bounce Rates 31% Mobile CVR Increase 11% tui.se
  32. Before After Defer Google Tag Manager Custom Third-Party Replace A/B

    Testing Library 50% Reduction domComplete 100KB JS shaved from homepage
  33. Lazy-load Third-Party Embeds Lighthouse Performance Score (27 to 96) +69

    chrome.com Reduction in Time to Interactive (13.6s to 3.1s) 10s Smaller JavaScript Payload 511KB Before After Eagerly load YouTube embeds on page load. Lazy-load YouTube embeds on user interaction.
  34. Remove expensive libraries There are other options too: • Deprecate

    • Replace • Defer • Update moment.js Bootstrap jQuery
  35. lodash → lodash-es momentjs → date-fns fetch → fetch-unfetch async

    → async-es babel-preset-es2015 →babel-preset-env Replace expensive libraries The one function you care about Importing entire module
  36. Defer dependencies • Tokopedia serves a “lite” version of their

    app to new users. • Lite version uses Svelte; service workers precache their React app in the background. React Version Svelte Version vs 320 KB 37 KB JavaScript required to render above-the-fold content
  37. Update dependencies Improvement in load time 100ms Uplift in revenue

    per session 0.7% • Zalando noticed that an older React version was impacting load times. • Updated from React 15.6.1 to 16.2.0.
  38. shopping.google.com Above-the-fold-rendering Interactivity for initial view Additional Search features Optimize

    UI Interactivity JS JS JS Tested on a Moto G4 over 3G Time to Interactive 4.5s Lighthouse Performance 90
  39. JavaScript Code-splitting Smaller JavaScript bundle 69% Faster Time-to-Interactive 28% Reduction

    First-Input-Delay 30% Walmart Grocery split up their JS bundles using granular code-splitting. They removed old / duplicate dependencies and cleaned up A/B test configs. Core bundles are cached with SWs for bytecode caching. grocery.walmart.com
  40. Route-based code-splitting <link rel=preload> 50% 36% <link rel=dns-prefetch> 18% *improvements

    from Twitter Lite traces, 2017. **This and other optimizations for Twitter Lite’s launch led to a 75% increase in tweets sent. Time to Interactive improvements
  41. Review what your tools output! Efficiently bundling i18n for 34

    languages Each build included i18n strings invalidating file hashes across the app. Each deploy invalidated your cache. Service Worker had to redownload everything. Issue caused by the tools. Generated files of same size, but different hashes.
  42. Lazy-load Translation strings Reduction in overall bundle size 30KB Saved

    loading Emoji picker on-demand 50KB mobile.twitter.com Internationalization tooling improvements Faster JS exec from new i18n pipeline 80%
  43. JavaScript For first-time users Increase in day one plays 54%

    mWeb users converted to native app installs 30% This and other investments in the mobile web player led to... First time users trying to play a song get a fast sign-up 55KB 300KB Once signed up, the mobile web player is lazy-loaded ~5s Time to Interactive on a Moto G4 over 4G
  44. Spotify improved perceived performance Reusing data from one page to

    hydrate next, reduced API calls & kept blank pages minimum Enables quick navigations between pages, even on slow connections.
  45. PRPL Pattern Interactive in 18KB JS Faster Time to Interactive

    82% This + other work Improvement in conversions 25% Speed jabong.com Push minimal code to get interactive Render fast Precache other routes (SW) Lazy-load JS On demand HTML CSS JS JS JS JS
  46. Invisible Text By default, if a font is not loaded,

    the browser will hide text for up to: 3 sec 3 sec 3 sec ∞ Flash of Display font immediately
  47. @font-face { font-family: 'Google Sans'; src: url(...GoogleSans.woff2) format('woff2'); font-display: swap;

    } Fonts chrome.com This & other techniques: Improvement in mobile page load times 20% Improvement in “Visually Complete” on 3G .5s
  48. Decrease in Latency 0.7s Preconnect Critical Origins .com Resource Hints

    Faster Time to Interactive 1.5s Preload Critical Scripts Faster Time to Interactive For future pages 2.7s Prefetch Visible Links bit.ly/quicklinkjs
  49. Prefetch Top Search Results Faster Median Above Fold Time (similar

    to FMP): 1.1s -> 0.39s 759ms eBay prefetch the top 5 items in search results pages for fast subsequent loads. This happens during idle time with requestIdleCallback(). “eBay saw a positive impact on conversions from prefetching”
  50. Predictive Asset Prefetching Home Search Item eBay are doing predictive

    prefetching of static assets. Home prefetches assets for Search. Search prefetches for Item page (and so on). Machine Learning & Analytics based prefetching us under consideration.
  51. Prefetch Popular Articles Faster article fetch time 78% Article impressions

    +13% (after 1 week) +26% (after 2 weeks) Virgilio Sport prefetch articles based on analytics data. Service Workers and <link rel=prefetch> prefetch the most clicked posts for users not on 2G. SW updates the top articles every 7m. After 3 weeks 45% Speed Prefetched
  52. Fold Inline critical CSS; deliver document in <= 14KB. Load

    CSS asynchronously. • filamentgroup/loadcss • addyosmani/critical • filamentgroup/criticalcss • pocketjoso/penthouse Critical CSS Other CSS
  53. Determining Critical CSS Common factors: • URL • Device viewport

    Nikkei also dealt with these factors: • Login state (logged in/out) • Subscription (paying/free) • Paywall (on/off) • 300KB of “critical” CSS Challenge:
  54. Application Server Critical CSS Server User CSS Document with inlined

    CSS CDN Dynamic Critical CSS API returns critical CSS based on parameters like user state and page. ...?service=article <style></style>
  55. User CSS Document with inlined CSS CDN Application Server Critical

    CSS Server Edge Side Inclusion (ESI) Caching Inlining ESI <style> params Markup language for assembling documents; CDN feature. Example: <esi:include src="/critical.css?service=article">
  56. • +58% conversions (subscriptions) • +49% daily active users Dynamic

    Critical CSS Improvement in FCP 1s Reduction in inlined CSS 80% This & other techniques: (without ESI) • 2x page views per session • 2.3x organic traffic
  57. Brotli: Reduce bytes over the wire Compress JS & CSS

    JavaScript reduction 15% Latency Improvement (P50) 37% Decrease in payload size for P75 sized payloads 90% Reduction globally for P95 load latencies. ~15-20% in Emerging Markets 5% Compress API Responses
  58. Before Adaptive Serving Adapt based on network quality & data

    signals After Reading news navigator.connection.effectiveType // 3g navigator.connection.saveData // true
  59. mBasic Site for low-end mTouch mBasic Time To Interactive 1.6s

    mbasic.facebook.com m.facebook.com Facebook offer a basic version of their site for low-end devices. It has no JavaScript, very limited images and uses minimal CSS with tables mostly for layout.
  60. Data Saver Mode For Images Reduction for data-usage from images

    on web. 96% incl disabled video autoplay 80% Twitter’s Data Saver mode presents images as a preview. The web renders a 64x64 blurred image (LQIP style). Users can tap to load large images. Previews load quick on 2G & 3G. Reduction for data-usage from images on iOS + Android 50%
  61. Client-side compression of photo uploads Reduction in cancelled photo uploads

    ~9.5% On the server, compresses images to JPEG quality 85% & max dimensions of 4096x4096px. On the client, check if images appear above max dimensions or an MB limit. If so, draw to <canvas> & output quality=85%.
  62. Adaptive Serving (Experiment) Before After eBay are experimenting with adaptive

    serving using effectiveType. On a fast connection, features like product zooming will load on demand. On slow connections, they won’t.
  63. 1. Lazy-loading 2. Responsive Images 3. Image CDNs 4. Defer

    Third-party JS 5. Remove costly libraries 6. Code-Splitting 7. Avoid FOIT 8. Prefetch 9. Preconnect 10. Preload 11. Critical CSS 12. Brotli 13. Adaptive Serving Get fast web.dev/fast Stay Fast bit.ly/lightwallet-docs Performance Budgets LightWallet