Building Forms That Don't Fight the Browser
A colleague asked me last week why our team cares so much about Core Web Vitals. I gave a rambling answer, so here is the longer, more organized version.
The first symptom was subtle. Nothing failed outright, but Core Web Vitals kept drifting in the wrong direction, and every fix seemed to make the next one harder.
The problem
Most of the complexity was accidental. Over the years we'd layered three different approaches to Core Web Vitals on top of each other, and each one made sense at the time.
We had good intentions. We even had a document describing how Core Web Vitals should work. The trouble was that the document and the code had quietly gone their separate ways.
How it works
The key insight was to treat Core Web Vitals as part of the interface rather than an implementation detail. Once we did that, most of the other decisions followed naturally.
Here's a trimmed-down version of what we ended up with:
export function debounce<T extends (...args: any[]) => void>(fn: T, wait = 200) {
let timer: ReturnType<typeof setTimeout> | undefined;
return (...args: Parameters<T>) => {
clearTimeout(timer);
timer = setTimeout(() => fn(...args), wait);
};
}
It's tempting to generalize this into a library. Resist that urge until you've needed it in at least three places.
A few things that helped along the way:
- Agree on what "done" means before the first pull request.
- Delete dead code aggressively; it's the cheapest refactor there is.
- Keep the old path around until the new one has survived a real incident.
- Make the default the safe choice, not just the documented one.
What it cost us
None of this is free. The new approach is a bit more verbose, and newcomers occasionally ask why we don't just use Svelte for everything. That's a fair question with a boring answer: we tried, and it made Core Web Vitals harder to reason about.
The biggest cost was social, not technical. Changing how people think about Core Web Vitals takes much longer than changing the code.
What's next
Six months in, nobody on the team wants to go back. That's about the best review a change like this can get.