Every client I work with says they care about speed, and almost every one of them discovers their site is slow only after a stakeholder loads it on hotel wifi. Performance is not a technical hobby. It is the first impression your product makes, and it is measurable long before anyone reads your copy.
Measure the thing users feel
I start every performance engagement with field data rather than a lab score. Synthetic audits on a fast laptop with a fibre connection produce comforting numbers and misleading conclusions. Real users arrive on mid-range phones, on congested networks, with a browser holding thirty other tabs, and their experience is the only one that converts or bounces.
The three metrics that matter map cleanly to human complaints. Largest Contentful Paint answers ‘when did something useful appear?’ Interaction to Next Paint answers ‘did the page respond when I tapped?’ Cumulative Layout Shift answers ‘why did the button move as I reached for it?’ I set a budget for each at the start of a project, and I treat a budget breach the same way I treat a failing test — as something that blocks a release, not something to file for later.
Budgets need to be visible to non-engineers, so I put them on the same dashboard as signups. When a marketing team can see that a new hero video cost eight hundred milliseconds of LCP, the discussion about whether to keep it becomes rational rather than territorial.
The work that actually moves the numbers
Images are almost always the first win. Serving modern formats, generating the right sizes, setting explicit width and height to reserve layout space, and lazy-loading anything below the fold routinely takes a second off a landing page. The hero image is the exception: it should be preloaded and never lazy, because it is usually the very element the LCP metric is measuring.
Fonts are the second. A custom typeface loaded without a fallback strategy hides your text at exactly the moment the user is deciding whether to stay. I subset aggressively, self-host to avoid a third-party connection, use font-display swap, and pair the webfont with a metrically similar system fallback so the reflow is barely perceptible.
JavaScript is the third and hardest. The fix is rarely a clever optimisation; it is deletion. Audit the bundle honestly and you will usually find a date library imported for one format call, an animation package used on a single fade, a carousel nobody scrolls, and an analytics stack with three overlapping tools. Server rendering the initial view and hydrating only the genuinely interactive islands is worth more than any amount of micro-tuning, because code that never ships to the browser cannot block the main thread.
Keeping it fast after launch
Performance regresses by default. Someone adds a chat widget, a tag manager grows a new script, a component gains a dependency, and three months later the site is heavier than it was on launch day. The only durable defence is automation: a bundle-size check on every pull request and a scheduled field-data report that a human actually reads.
I also keep a written list of trade-offs the team has already accepted, with the cost of each. When a stakeholder asks for a heavyweight embed, the answer is not no; it is ‘this costs roughly four hundred milliseconds on mobile, and here is what we would remove to pay for it.’ That framing turns performance from an engineering preference into a shared budget everyone can spend deliberately.
None of this requires exotic tooling. It requires deciding that speed is part of the specification, writing the number down, and defending it with the same seriousness you would defend a security requirement. Sites that stay fast are not built by faster developers — they are built by teams who made speed a non-negotiable line item and never quietly removed it.