Performance 1
In terms of speed, reliability, and responsiveness
Intro
In client/server relationships, performance level quality is determined by:
Server software, hardware, application architecture
Amount of data transmitted up/downstream
What transmitted data asks the recipient to do
Client hardware, network connection speed
Demand volume
Adequate performance levels are for serving valued clients with the slowest devices and connections. Everyone else gets a faster experience, and hopefully very few clients won't get a usable one at all.
User Experience
Regarding UX, how do the following areas of performance...
Area
Factors
Speed
Network communication, application architecture, code efficiency, data volume, caching
Reliability
Service availability; consistently meeting expected behavior
Responsiveness
Code efficiency, providing timely feedback during interaction
...for each 1sec of delay affect:
User abandonment
Organization goals
Word of Mouth
Symptoms:
Speed (actual v. perceived)
Flash of unstyled content (FOUT)
Content shifting
Scrolling is janky or frozen
TODO For info on responsiveness and feedback, see this section on UX (LINK)
Application Architecture
While a variety of architectures can serve the same purpose, increased network demand requires doing one or more of the following at a high level:
Upgrade hardware
Replicate the application and its services
Decouple application components and create workflows
Use concurrent systems with actors or channels
As systems grow, the key to any architectural performance improvement is to know whether it's needed, and if so, where. This is handled through cloud service dashboards, designated observability points, and DevOps personnel.
The keys to optimizing production and delivery are:
Understanding data demand patterns and usage needs
Create and deliver static data by default (made-to-stock)
Store data in caches and CDNs and restock at given or calculated intervals
Create and deliver dynamic data when necessary (made-to-order)
TODO: client architecture
Servers managing routes, application logic, delivering web pages
Clients managing routes, application logic, resources plus possible need for server-side rendering.
What are the performance implications? Vaguely:
Traditional is faster for first loads, slower on subsequent
SPAs are slower for first loads, faster on subsequent
In the end, perhaps can take it for granted that the server (organization) uses a decent stack with a CDN for cached files and a host that reasonably handles demand. That leaves just optimizing the data transmitted within a server's control.
Frontend Considerations
Devices and Network Speeds
PC -> slow or fast connection Mobile -> slow or fast connection Test on representative, average mobile devices because JS parsing times can be up to 3x as slow as the newest, premium phones.
Time to Interactive
TTI is the total time in seconds from first request to being ready for user interaction. It entails first resolving the address, making the initial request, and receiving the HTML.
After loading HTML, it is synchronously parsed from top to bottom, pausing to download and parse any CSS and JS. Images are added to a queue for later downloading.
Parsing CSS is fast but large JS bundles of 1MB can take an average of 700ms (2017). Painting begins and finally a page will be interactive.
The Stages are as follows:
Initial Request
First Paint - anything other than a white screen
First Contentful Paint - logos and graphics start to appear
First Meaningful Paint - visible text
Visually Complete - when viewport content finishes loading and rendering
First Interactive - Scrolling possible and buttons/link work
With variable network connections and device CPUs, placing the software architecture on the client can have significant performance penalties. While client-side asset caching and subsequent server-side rendering do help, the initial loading and parsing times are still quite important. After all, product performance is all that matters to users.
So regardless of whether clients initially load/parse individual webpages or view applications, the time-to-interactive stage should be minimized to an acceptable level, based on product expectations.
TODO starting network requests using "navigationPreload" while Service Workers are booting up
Network Communication
Intro
Being aware of how HTTP communication works, web developers can influence performance architecting their applications to spend less time making network connections, and sending more data with each each request.
For more info, see TODO LINK
Process: DNS Lookup Server Connection Server Request Server Response
DNS Lookups
TODO Lookup time for each domain. Can premium DNS providers help? At least with security and availability Supposedly large hosts DNS services are slow, whereas those from Google, AWS, etc. are fast. Consider how cellular connection speeds can be slower and inconsistent affect each request time. Navigation Timing API can be helpful?
Preloading in HTML Head
DNS-Prefetch link rel="dns-prefetch" presolving DNS for primary resource origins
Preconnect link rel="preconnect" "Gives a hint to the browser to begin the connection handshake (DNS, TCP, TLS) in the background to improve performance" This is particularly good for embedded content that is loaded/streamed from 3rd parties.
Prerender link rel="prerender" Unfortunately downloads an entire page, executes arbitrary JS in the background, and uses a lot of CPU. Google AMP works around this by prerendering only the first viewport, without executing any 3rd party JS.
Preload link rel="preload" as="content-type" (script, other. See HTMLLinkElement.as) "browsers can be informed to prefetch resources without having to execute them, allowing fine-grained control over when and how resources are loaded." The "as" property allows browser to customize load optimization based on resource type (video, audio). Cuts round-trips, supports cross-origin requests, has load/error events, content negotiation.
For preloading a chunk of a large resource, use as="fetch". Then this can be used with Media Source Extensions, a well-supported JavaScript API that lets you build streams for playback from segments of audio or video. Netflix, Twitch, and YouTube use this extensively to quickly play video.
Allows you to force the browser to request a resource, while the html is still downloading, without blocking the window.load event. Such resources are stored in the browser and are inactive until explicitly referenced in the DOM, JavaScript, or CSS. If the preload request hasn't completed by the time the resource is needed, a normal network request will be sent.
Preloading in HTML Body
Element attribute preload="auto". Note: Such resource fetching will only begin when the initial html document has been completely loaded and parsed. The window load event will fire when the resource has been fetched.
Note: This is just a hint to the browser and is subject to compatibility, and whether users have enabled "save-data" in their browser settings to limit bandwidth consumption.
Links can be preloaded right before they're clicked using libraries like Instant.Page and InstantClick
Preloading with Service Workers
starting network requests using "navigationPreload" while Service Workers are booting up
Respectful Preloading
Preloading requires battery life, memory, network connection type, possibly a video poster image, and device storage from users. Preloading may not be in their interest. We can use progressive enhancement to test for specified conditions in the user browser, and decide to adjust preloading (ex: return lower quality media) or dismiss preloading altogether. Device browser attributes to test include: navigator.{getBattery, deviceMemory, storage, connection}.
Two other ways to test whether perhaps resources should load. The Page Visibility API can tell if a document is currently visible on the display or not. The IntersectionObserver allows us to track when an element enters or leaves the browser viewport. If there's many large media assets on the page, resetting the video src attribute to null on viewport exits can significantly decrease the page's bandwidth consumption. Particularly important for webpages using infinite scroll.
Lastly, one can check if users have enabled "Save Data" in their browser, and then decide to preload accordingly.
Resource Locations
Your server, bundled or not CDNs Cache
Load Considerations
Think about when/how to load assets Only load assets when they add value Lazy load non-critical resources
Progressive Rendering
Stream HTML responses
Use HTTP/2 Server Push
Single connection and handshake with server pushing additional resources, eliminating subsequent round trip communication costs for each resource.
Although great, it's not cache aware and has no prioritization options. Two options include:
Cache digests
Service Workers
Volume
No dev code to production
Code Tips
Flexbox or Grid instead of CSS layout frameworks and/or JS. Semantic HTML to possibly reduce the total number of code characters CSS selector-based DOM traversal without using JS DOM libraries.
Removing Unused Code
CSS/JS, Tree Shaking (bundler)
Selective loading, serve modern browsers ES2015
Network Connection API
Not widely supported outside Chrome, but it allows to check by type (wifi, 2G, etc), which allows us to load resources accordingly.
User Preferences
Perhaps users prefer faster performance or simpler experiences, and would rather opt-in to rich media through user settings (saved in SW) and/or by default.
Users on slow networks often disable network access by default and enable it when ready to use the web. Or perhaps data plans are expensive, and it's courteous to respect.
Save-Data Request Header
Some browsers support providing a "Save-Data: on" attribute when making requests to web servers. Developers can take advantage of this by checking the headers or via JS.
Caching
Network
Anything to get resources closer to users: CDNs
Browser
TODO: Use include caching info from MDN, Google
Set Cache Headers
First load what's needed to get to interactive. Later load anything else foreseeably needed. Unused JS upfront just delays TTI. Code splitting allows creating smaller bundles whose loading we can prioritize.
Service Workers caching with IndexedDB
Resources
Intro
Only require resources which provide significant value. Otherwise it just increases loading/parsing time, wastes developer time, and perhaps negatively affects performance and UX. Assuming one has already tackled whether to use libraries, frameworks, or to build a single page application (SPA), all sites should first load:
Critical-path HTML, CSS, JS
Followed by optional:
Service Workers
Router, State management, Utilities
Framework library
Application logic
Fonts and Media
Can any resources can be inlined to the HTML for fewer network requests?
HTML
TODO Minify, pre-compress
Fonts
"font-display: optional" controls what fonts will render depending on their load time. If they take too much time over the network, they won't be used. If they're already available in the cache, use them.
Font caching?
Subset custom fonts to download only what will be used, though allowing common letters like umlauts and punctuation common in other languages in case data received or user-entered uses them.
Other considerations?
CSS
Inlining CSS means fewer external resource requests, although perhaps inappropriate for larger websites and style systems.
Using modular or composable CSS, you can reuse styles more often, and update only parts when needed, thus leaving the okay parts in the cache.
Compile CSS with decimal precision (ex: 4 places) Fallbacks for previous browser versions (ex: 2) with autoprefixer tools Shorthand declarations if possible Remove any unused declarations or rule sets Brotli and Gzip individual files, with normal file for unsupported file
JavaScript
First load what's needed to get to interactive. Later load anything else foreseeably needed. If non-SPA, load main JS at bottom of HTML document, followed by page-specific JS. To avoid race conditions and ensure dependencies available, keep main JS file as normal (synchronous) without async or defer.
Using "defer" as script tag attribute, "allows the external JavaScript file to run when the DOM is loaded, without delaying page load first."
Using "async" as script tag attribute, "allows the external JavaScript file to run when it's available, without delaying page load first."
requestIdleCallback
From MDN:
"Method queues a function to be called during a browser's idle periods. This enables developers to perform background and low priority work on the main event loop, without impacting latency-critical events such as animation and input response. Functions are generally called in first-in-first-out order; however, callbacks which have a timeout
specified may be called out-of-order if necessary in order to run them before the timeout elapses.
You can call requestIdleCallback()
within an idle callback function to schedule another callback to take place no sooner than the next pass through the event loop.A timeout
option is strongly recommended for required work, as otherwise it's possible multiple seconds will elapse before the callback is fired."
Unused JS upfront just delays TTI.
Code splitting into many bundles (ex: by route)
Write modular JS, grouped as appropriate (functionality, route) Not all visitors enter through same route, nor visit all routes. Some visitors browsers' may not support certain features, for which any associated JS shouldn't be loaded
Options: - Combine files based on folder structure - Use module closures to isolate from other JS in global namespace - Perhaps keep polyfills in separate files.
Tree-shaking to remove unused code. By examining during production builds what's actually used from an imported module, a compiler can reformat both important statements and module scripts to only include what get's used.
Minify / uglify
Bundling If Manual, concatenating may need to be ordered. Some tools like in gulp will use alphabetical order in folders. Renaming initial files with underscores, and last files with symbols like Omega can help code position in concatenated files.
If Automated, Code splitting allows creating smaller bundles whose loading we can prioritize.
Statistics (2017 Chrome Dev Summit), show that JS is requested from memory and disk only ~50% of the time. Fonts also perform poorly. This could be for various reasons including:
New releases are pushed too often, thereby invalidating cached data
Insufficient HTTP caching headers set
3rd Party JS
For ad content, consider sandbox iframes because they :
use synchronous scripts
document.write more sync scripts
do style recalculations which is heavy on CPU because it entails scanning your entire page, and named styles may override your styles
iframes limit their interaction to just their domain.
Media and Formats
Are They Needed?
Important question as they can take a lot of space in the total payload. Is it filler or stock that adds no extra value? While perhaps emotionally appealing, if the benefit is small, it'd be better served ONLY for fast connections. (TODO: Is that actionable?)
Ex: If an article is about net security, is a stock photo of a hacker really needed?
Another aspect to consider is how images add value to content blocks as headers. When screens are large, images can provide greater emphasis to important content blocks, or help differentiate them. However when screen sizes are small and blocks are vertically stacked, images may lose this supplementary value and their use should be perhaps limited to where they provide the greatest value.
TODO: discussed in Aaron Gustafson presentation and likely elsewhere There is an opportunity to use JS to insert images via data-img attributes, which could be appended to elements ??? depending on screen sniffing, media queries. Maybe the picture element is injected dynamically instead to allow multiple formats.
From Aaron Gustafson "Performance as User Experience":
Does the image reiterate info found in the surrounding text?
Is the image necessary to understand the surrounding content?
Does the image contain text?
Is the image a graph, chart, or table?
Could the content of the image be presented in a different format that wouldn't require an image?
Is the image purely presentational?
Formats and Compression
Refer to Images.guide for comprehensive info. But the gist is:
Automate media compression through build process, CDNs, or other providers. Build process tools include managers (Mac: ImageOptim) and format encoders.
Remove metadata to save size. Use SVG for non-photo graphics and animation. In code, include fully supported formats first, followed by modern formats.
Type
Formats
Encoding
Age
Video
WEBM
VP9
Modern, good support
Video
MP4
H.264
Full support
Photo
WEBP
VP8
Modern, good support
Photo
JPG
MozJPEG
Full support
Illustration
SVG
various
BEST, Full support
Illustration
PNG, GIF
various
Full support
Illustration
Base64
various
Full support
Animation
WEBM
VP9
Modern, good support
Animation
SVG
various
Full support
Additional Tactics
Consider preloading and lazy loading to prioritize assets. Fore preloading, see previous section under Network Communication.
Consider selective serving media through HTTP accept-headers Use tools to generate media srcset breakpoints Use client-hints for automated browser asset selection (good support)
Troubleshooting
FOUT
Consider applying 0 body opacity until JS runs and resets it. If there's no JS, include a noscript tag to unhide the body. If JS servers take too long, you can apply a timeout rule and CSS keyframe animate the body from 0 to 1.
Content Shifting
Use placeholders for known ad spots with static resource sizing.
Janky / Frozen Scrolling
Calculate restyles only when things resize Batch DOM accesses and changes low styles recalcs per frame, and per page load.
Slow Animations
Transforms and Opacity is best handled by CSS for hardware accelerated performance (GPU), sequentially timed animations by JS.
Last updated