# Measure Reliably

**MUST** · **ID:** `performance-measure-reliably` · **Category:** performance
**Source:** [Vercel](https://github.com/vercel-labs/agent-skills/blob/main/skills/web-design-guidelines/SKILL.md)
**Interactive version:** https://ui-guides-agent-rules.netlify.app/principles/performance-measure-reliably

> MUST: Measure reliably (disable extensions that skew runtime)

Profile in a clean browser profile with extensions disabled

Extensions run content scripts on your page, on your main thread, inside your measurements. Ad blockers rewrite the DOM, password managers scan every form, React DevTools instruments each render — all of it lands in the same numbers you are about to draw a conclusion from. The damage is not that the timings are slower but that they are noisy: when run-to-run spread is larger than the regression you are hunting, the profile stops being evidence. Measure in a clean profile or incognito with extensions off, take several runs, and compare spreads rather than a single number.

## Bad — do not do this

`performance-measure-reliably-bad`

```tsx
import { useState } from 'react';

/** The workload under test — identical in both examples. */
function workload() {
  let n = 0;
  for (let i = 0; i < 400_000; i++) n += Math.sqrt(i);
  return n;
}

/** Stands in for a content script an extension injects into every page. */
function extensionOverhead() {
  const spin = 2 + Math.random() * 14;
  const until = performance.now() + spin;
  while (performance.now() < until) {
    /* the extension is doing its own thing, on your main thread */
  }
}

export function MeasureReliablyBad() {
  const [runs, setRuns] = useState<number[]>([]);

  const measure = () => {
    const next: number[] = [];
    for (let i = 0; i < 5; i++) {
      const t0 = performance.now();
      workload();
      extensionOverhead();
      next.push(performance.now() - t0);
    }
    setRuns(next);
  };

  const spread = runs.length ? Math.max(...runs) - Math.min(...runs) : 0;

  return (
    <div className="w-full max-w-sm space-y-4">
      <div className="bg-card border border-border rounded-lg p-4 space-y-3">
        <p className="text-xs text-muted-foreground">
          Profiling in the everyday browser — 3 extensions still enabled
        </p>
        <button
          onClick={measure}
          className="px-3 py-1.5 text-xs bg-primary text-primary-foreground rounded-md focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring"
        >
          Run benchmark ×5
        </button>

        {runs.length > 0 && (
          <div className="space-y-1 font-mono text-xs">
            {runs.map((r, i) => (
              <p key={i} className="text-muted-foreground">
                run {i + 1}: {r.toFixed(1)} ms
              </p>
            ))}
            <p className="text-error pt-1">spread: {spread.toFixed(1)} ms</p>
          </div>
        )}
      </div>
      <p className="text-xs text-error">
        The same code, five times, with a wide spread. You cannot tell a real
        regression from the extension&apos;s noise — so you optimize the wrong thing.
      </p>
    </div>
  );
}
```

## Good — do this

`performance-measure-reliably-good`

```tsx
import { useState } from 'react';

/** The workload under test — identical in both examples. */
function workload() {
  let n = 0;
  for (let i = 0; i < 400_000; i++) n += Math.sqrt(i);
  return n;
}

export function MeasureReliablyGood() {
  const [runs, setRuns] = useState<number[]>([]);

  const measure = () => {
    const next: number[] = [];
    for (let i = 0; i < 5; i++) {
      const t0 = performance.now();
      workload();
      next.push(performance.now() - t0);
    }
    setRuns(next);
  };

  const spread = runs.length ? Math.max(...runs) - Math.min(...runs) : 0;

  return (
    <div className="w-full max-w-sm space-y-4">
      <div className="bg-card border border-border rounded-lg p-4 space-y-3">
        <p className="text-xs text-muted-foreground">
          Profiling in a clean profile — extensions disabled
        </p>
        <button
          onClick={measure}
          className="px-3 py-1.5 text-xs bg-primary text-primary-foreground rounded-md focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring"
        >
          Run benchmark ×5
        </button>

        {runs.length > 0 && (
          <div className="space-y-1 font-mono text-xs">
            {runs.map((r, i) => (
              <p key={i} className="text-muted-foreground">
                run {i + 1}: {r.toFixed(1)} ms
              </p>
            ))}
            <p className="text-success pt-1">spread: {spread.toFixed(1)} ms</p>
          </div>
        )}
      </div>
      <p className="text-xs text-success">
        Same workload, a much tighter spread. Now a change that moves the number
        is a change you actually made.
      </p>
    </div>
  );
}
```

## References

- [Vercel Web Interface Guidelines](https://github.com/vercel-labs/web-interface-guidelines)
- [Chrome DevTools: Performance features reference](https://developer.chrome.com/docs/devtools/performance/reference)
