In short

Performance work is diagnosis, not a checklist. On the platform I spent this week measuring, the two changes that mattered most were reverting a newer image format back to an older one, and repairing a cache purge that had been quietly doing nothing for days. The DOM cleanup I was proudest of moved the metric by exactly nothing. What follows is what the measurements actually said, including the parts that made me look silly.

A slow page costs you the visitor who was already interested. They tap through from a search result, watch a white screen for four seconds, and go back. You paid to put that result in front of them and the page lost them before it said anything. It costs you rankings too, quietly, because Core Web Vitals feed into where you place. And it costs you internally, because a team that finds its own site sluggish stops using it properly.

The usual response is a checklist. Install a cache plugin, move the images to the newest format, minify everything, lazy load the rest. That is not optimisation, it is decoration, and it is why so many sites end up with four plugins fighting each other and a score that has not moved. Every item on that list is sometimes right and sometimes actively harmful. The only way to know which is to measure the specific site in front of you.

Here is how that went on one engagement, the platform for Ampy, across a week of measurement. In order, wrong turns included.

The newer image format made the site slower

The hero image was being served as AVIF. AVIF is the newer format, it compresses harder than WebP, and a smaller file is supposed to paint sooner. That is the reasoning every performance checklist gives you, and it is the reasoning I used.

Largest Contentful Paint then refused to come down for a week. I went after the usual suspects: DOM size, CSS delivery, render-blocking work in the head. None of them shifted it by anything I could tell apart from noise.

The cause was the format itself. Lighthouse’s mobile profile applies a 4x CPU throttle, and AVIF costs considerably more to decode than WebP does. The AVIF hero was saving about 19 KB of transfer and paying that saving back many times over in decode time on a deliberately slowed processor. Switching the hero back to WebP brought the paint down.

Two honest caveats, because the finding is less heroic than it looks. The throttle amplifies the effect, so a recent phone on a good connection will not see the full swing. And the audience that does see it, older Android hardware, is real, and is usually the slice of your traffic you are least likely to be testing on. The direction holds either way, and the lesson travels further than image formats: a smaller file is not automatically a faster paint, and once the processor is the bottleneck rather than the network, decode cost can swamp download cost entirely.

Pro tip

Before you roll a newer image format across a site, serve the same page in both formats and measure the paint, not the payload. File size is the easy number to look at, and it is not the number your visitor experiences.

The cache was reporting success and doing nothing

The purge routine on that platform called a method that no longer existed in the current version of the cache plugin, and the resulting failure was caught and thrown away. Every “cache cleared” the admin reported was a lie. It had been lying for a while and nobody had noticed, because the message it printed on failure was the message it printed on success.

The second half was worse. The cache directory had at some point become owned by root, so the preloader could not write into it. It ran for seven and a half minutes, wrote zero pages, and raised no error anywhere: not in the plugin log, not in the PHP error log, not on screen. Real visitors were being served uncached PHP on every request, and I was taking performance readings against a site I believed was warm.

That is the failure mode worth designing against. A cache operation that cannot fail loudly will eventually fail quietly, and then you spend a week optimising against numbers that describe a state none of your visitors were ever in. The code fix was small. The process fix was the bigger one: a purge now checks afterwards that the thing it purged is actually gone, and the preloader reports a page count that a human reads before any measurement is believed.

Field note

Confirm the cache is warm before you trust a single number

Every figure further down this page was taken against a verified warm cache with 832 pages preloaded, and that verification step exists only because the preloader had already fooled me once. Measuring first and verifying later gets it backwards. You cannot tell a cold-cache run from a slow site by looking at the score.

The measurement tool will lie to you as well

PageSpeed Insights caches its results. I called it four times in a row and got back byte-identical output, matching to the millisecond on every metric. At a glance that reads as a beautifully stable site.

It is one sample repeated four times. Zero spread is the tell, because a real site does not produce identical timings twice, let alone four times running. The fix is unglamorous: space samples roughly two minutes apart, take enough of them, and report the median with the range next to it. The final figures for that platform come from repeated runs taken that way, with no duplicate responses anywhere in the set, and the raw JSON kept.

Related, and more embarrassing. A comment in the codebase asserted a past measurement that I then could not reproduce in four attempts. I trusted it because it was written down, and it sent me chasing DOM size and CSS delivery, neither of which moved the metric at all. A single-sample performance note in a comment is not evidence. On a site whose score legitimately swings between runs, it is barely an anecdote.

The optimisation that did not work

The testimonials slider was rendering each star rating as ten SVG nodes, a filled star and an outline star stacked at each of five positions. I rebuilt it to draw all five stars as a single path with an offset applied per star, taking every rating from ten nodes to two. The slider’s loop mode was also cloning slides with no cap, so the document carried several copies of content nobody would ever scroll to. I capped the clones.

It measurably reduced the DOM. It did not move LCP. Not slightly, not within noise, it moved nothing I could separate from run-to-run variation.

I am putting it on the page because a write-up that lists only the changes that worked teaches you the wrong thing. The DOM work was correct on its own terms, the markup is lighter and the slider is easier to maintain, but it was not the bottleneck. Had I shipped it and stopped there, I would have declared victory on a metric that had not moved. Knowing which changes did nothing is how you find out where the bottleneck actually is.

A performance engagement that reports only its wins is not reporting. Half the value is the list of things that were supposed to help and did not.

What the numbers came out at

These are the figures from the final measurement round on that platform’s homepage: repeated PageSpeed Insights runs, verified warm cache, spaced about two minutes apart, no duplicate responses.

Metric Measured result
Desktop PageSpeed score 98 median, with every run landing 97 to 99
Desktop Largest Contentful Paint 978 ms median, 821 ms to 1,121 ms
Desktop First Contentful Paint 361 ms median, 356 ms to 367 ms
Desktop Speed Index 784 ms median
Cumulative Layout Shift 0.001 desktop, 0.000 mobile
Homepage HTML weight 330 KB raw, 76 KB over the wire

Desktop is done. Every run in the final set came back 97, 98 or 99, First Contentful Paint varied by 11 ms across five runs, and the layout does not shift.

The longer write-up of that platform is in the case study, and the caching architecture behind those numbers is explained in full in how to make a heavy WordPress site load like it is static.

What this actually buys you

Nothing on this page is clever. Verify the state of the system before you measure it. Take enough samples that you can see the spread instead of one number. Change one thing. Measure again the same way. Keep what moved the metric, and write down what did not, because that second list is the map of everywhere the bottleneck is not. Follow that sequence and you end up with a site that got faster. Skip it and you end up with a site that got a plugin.