#a11y#testing

How We Test UI Without Snapshot Tests

· 3 min read

There is no shortage of articles about React. Most of them cover the happy path. This one is about what happened after the happy path ended.

We had good intentions. We even had a document describing how hydration should work. The trouble was that the document and the code had quietly gone their separate ways.

How we got here

When we finally measured, the numbers were worse than anyone guessed. Around 33% of the time spent on a typical change went into working around hydration rather than building anything.

Most of the complexity was accidental. Over the years we'd layered three different approaches to hydration on top of each other, and each one made sense at the time.

How it works

The key insight was to treat hydration as part of the interface rather than an implementation detail. Once we did that, most of the other decisions followed naturally.

The core of it looks like this:

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);
  };
}

This alone let us delete a surprising amount of code elsewhere in the project.

A few things that helped along the way:

  • Keep the old path around until the new one has survived a real incident.
  • Delete dead code aggressively; it's the cheapest refactor there is.
  • Pair on the first few changes so the knowledge doesn't live in one head.

Where this breaks down

There are cases where this falls apart. If your team is very small or your traffic is very spiky, the extra moving parts may not pay for themselves.

We still haven't solved design tokens. That's a post for another day, and honestly, I'm not sure we'll get it right the first time either.

What's next

If you take one thing away from this, let it be this: hydration is a design decision, not a cleanup task. Make it early and make it on purpose.

NA
Noah Achebe
Frontend engineer in San Diego. Writing about frontend engineering.