2024 – 2025 · Senior Engineer · Frontend
Cutting frontend load time across a multi-brand publisher
Owned the perf work across a portfolio of consumer surfaces — image pipeline, render-blocking audits, hybrid app state-sync, and the Lighthouse-CI budgets that keep it from regressing.
- Core Web Vitals
- React
- Hybrid apps
- Performance
- RUM
The brief
A portfolio of consumer-facing web properties — news, lifestyle, commerce — all sharing a common platform layer, all showing different shades of the same Core Web Vitals problem. LCP was slow on key landing pages, CLS was jumping on the surfaces that monetised, and INP regressions were sneaking in unnoticed because the platform team didn't have a budget that broke a build.
I owned the perf work across the frontend stack — image pipeline, render path, third-party scripts, and the budgets that keep it from regressing.
LCP
−57%
target ≤ 2.5s · passing
CLS
−88%
target ≤ 0.1 · passing
INP
−61%
target ≤ 200ms · passing
Before
32%
passing
After
87%
passing
What I shipped
Responsive image pipeline. Audited the asset pipeline end-to-end.
Replaced unbounded image dimensions with breakpoint-tuned srcset, pinned
width/height on hero images to kill layout shift, and routed above-the-fold
images through a scaler service that optimised at edge.
Render-blocking audit. Walked through the critical path on the slowest templates with the team. Pulled third-party scripts off the critical path, deferred consent-management chains where the contract allowed it, and inlined the small slice of CSS that actually paints above the fold.
Hybrid app state-sync rebuild. The mobile surface ran the web app inside a WebView with a native state singleton outside. State updates were getting dropped across reloads, and the lifecycle on re-entry was firing effects against stale closures. Fixed by formalising a state-rehydration contract that handled SPA-route and full-reload re-entries explicitly.
Perf budgets in CI. A Lighthouse-CI gate that breaks builds when LCP, CLS, or INP regresses past a threshold. Wired to the existing PR pipeline so it lands before the change does, not after. The hardest part wasn't the tool — it was negotiating the budgets that the product teams would actually defend.
RUM dashboards that engineers read. A real-user-monitoring view segmented by device, connection, and template — replacing three disagreeing lab dashboards with a single source of truth. Easy to look at, easy to drill into, hard to lie to yourself with.
What this unlocked
- Measurable, sustained LCP and CLS improvements across the surfaces that mattered most — landing pages, article reads, conversion funnels.
- A perf budget that broke a build before regressions reached production, rather than a dashboard nobody checked.
- A documented hybrid-app re-entry contract that other teams could ship against, instead of debugging the same state-staleness bug every quarter.
- A team habit of reading real-user data before believing lab-test improvements.
Lessons I keep coming back to
Performance is a budgeting problem, not a one-shot optimisation problem. The work isn't over when LCP improves — it's over when LCP can't silently regress.
- RUM beats Lighthouse. Lab metrics are a starting point. The number that matters is what your real users actually feel, segmented by device and connection.
- Layout shift is mostly a discipline problem. Pin dimensions on every image, every ad slot, every dynamically-injected component. The hard part isn't fixing it — it's preventing the next regression.
- The state-sync contract is the bug. Stale closures across same-render effects, state surviving WebView reloads, route effects firing against pre-route state — same root cause: an implicit contract that nobody wrote down.
- Performance budgets live in CI or they don't live at all. A Slack channel of complaints about a regression is a budget that already failed.