Trudna część performance toola to nie AI. To wyciągnięcie liczb, którym można ufać, bez człowieka wklejającego je ręcznie.
Każdy performance audit, jaki kiedykolwiek oddałem, zaczynał się tak samo: otwórz stronę, odpal Lighthouse, przepisz liczby do dokumentu ręcznie. Pomiar był prawdziwy, ale workflow był ręczny, a ręczny workflow nie odpala się w CI i nie odpala się dwa razy tak samo. Cały sens budowania toola to zrobić pomiar automatycznym, nie robiąc go flaky.
Więc zanim zaufałem jakiemukolwiek findingowi, musiałem udowodnić jedno: że floor jest stabilny.
Czym jest floor
Pięć źródeł, wszystkie deterministyczne, żadne nie jest modelem.
Lighthouse odpala pięć razy, świeży headless Chrome co run, tool bierze medianę. Świeży Chrome co run, więc żaden warm-cache bias nie przecieka między nimi. Pięć runów, bo pojedynczy strzał Lighthouse ma realny run-to-run variance, a pojedynczy strzał to sposób, w jaki kończysz z oceną, która zmienia się za każdym spojrzeniem.
web-vitals odpala przez Playwright ze scripted interaction, produkując LCP, CLS, TTFB i INP z żywej strony. Oznaczam je jako synthetic-field, nie field, bo pochodzą ze scripted przeglądarki, nie od prawdziwego użytkownika. Rozróżnienie ma znaczenie i trzymam je widoczne.
Resource trace otwiera stronę w Playwright, scrolluje, daje się ustabilizować i czyta Resource Timing API plus buforowany long-task observer - rozmiary i timingi per request, render-blocking status prosto z Chromium, third-party origins, atrybucja main-thread blocking.
Bundle analyzer parsuje build stats z Vite, Rollup albo nuxi analyze i skanuje zależności bez instalowania czegokolwiek - rozmiary chunków, zduplikowane wersje, paczki zadeklarowane i nigdy nieużyte.
I statyczny pass po source łapie tę garść anti-patternów, które znajdziesz bez przeglądarki: obrazek bez wymiarów, deep watcher, runtimeCompiler: true. Rzeczy pewne. Bez interpretacji.
To jest floor. Nudna część. I część, na której stoi cała reszta - dlatego musiała być udowodniona pierwsza.
Dowód na variance
Odpaliłem pełny audyt na portfolio.sdet.it dwa razy. Mediana z pięciu za każdym. Jeden po drugim. Oto co wyszło.
| Audyt #1 | Audyt #2 | Δ | |
|---|---|---|---|
| Lighthouse score | 72 | 72 | 0 pkt |
| Werdykt CWV | PASS | PASS | ten sam |
| Oceny obszarów | same A | same A | te same |
| LCP mediana | 991 ms | 926 ms | 65 ms |
| Ocena | C | C | ten sam band |
Te same dwa runy, ułożone tak, jak drukuje je tool:
Run-to-run variance - https://portfolio.sdet.it
(Lighthouse median-of-5 + headless Chromium CWV, two back-to-back runs)
Run #1 Run #2
Lighthouse perf 72/100 72/100
LCP (lab) 991 ms 926 ms
CLS (lab) 0.000 0.000
TTFB 42 ms 34 ms
Core Web Vitals PASS PASS
bundle A (100) A (100)
runtime A (100) A (100)
network A (100) A (100)
ssr-hydration A (100) A (100)
assets A (100) A (100)
Werdykt jest stabilny jak skała tam, gdzie się liczy: identyczny Lighthouse score, identyczny PASS, identyczne oceny obszarów, identyczna litera. Surowe LCP zdryfowało 65 ms między runami - i to jest uczciwa, ciekawa część. Metryka pod spodem rusza się trochę run do run, jak zawsze na żywej sieci. Obie wartości siedzą wygodnie w środku bandu “good” Google, więc werdykt nigdy się nie chwieje.
Te 65 ms to dokładnie powód, dla którego tool odpala pięć passów Lighthouse i bierze medianę zamiast ufać pojedynczemu strzałowi. Pojedynczy strzał łapie tę liczbę, którą sieć podała ci w tej sekundzie. Mediana to to, co pozwala ocenie stać stabilnie, podczas gdy surowa metryka oddycha - i to różnica między liczbą, którą możesz postawić przed klientem, a liczbą, za którą musisz przepraszać.
Dwanaście runów Lighthouse przez dwa audyty. Zero timeoutów. Zero crashy. Deterministyczne mediany za każdym razem.
Teza całego toola - context before LLM - stoi na tej tabeli. Gdyby zmierzony werdykt się chwiał, to wszystko, co AI mówi na wierzchu, byłoby zbudowane na piasku. Nie chwieje się. Więc AI dostaje głos.
Pięciu specjalistów
Na floorze siedzi pięciu AI specjalistów, jeden na obszar: bundle, runtime, network, SSR/hydration, assets. Odpalają równolegle - jeden dispatch, pięciu agentów, każdy dostaje swój wycinek zmierzonego floora plus source, który musi przeczytać.
Każdy ma wąską robotę i Vue/Nuxt idiom za sobą, bo metoda przyszła z audytowania ecommerce na Vue i Nuxt - trzech produkcyjnych platform - nie z generycznej checklisty. Anti-patterny, na które polują specjaliści, to te, które realnie ugryzły prawdziwe koszyki i listingi produktów, nie podręcznikowe przykłady.
Bundle czyta rozmiary chunków i grafy zależności - luki w code-splittingu, zduplikowane wersje, paczki wysłane i nigdy nieużyte.
Runtime patrzy na koszt main-thread i Vue-specific pułapki: deep watchery, inline style object literale w v-for, które destabilizują propsy przy każdym renderze.
Network czyta resource trace pod kątem waterfalla - sekwencyjne awaity, które miały być równoległe, payloady pobierane bez filtrowania pól, third-party origins.
SSR/hydration patrzy na TTFB, strategię hydration, route rules, granice islandów.
Assets ogarnia obrazki, fonty, element LCP, bloat zestawów ikon.
Rzecz kluczowa: specjaliści czytają zmierzony output. Nie generują metryk. Liczba przyszła z Lighthouse i z trace. Specjalista wyjaśnia, czemu liczba jest, jaka jest, i co z tym zrobić. Pomiar to fakt. Interpretacja to robota modelu. Te dwie rzeczy nigdy się nie mieszają.
Problem nakładania się i protokół
Pięciu specjalistów patrzących na jedną stronę będzie deptać sobie po nogach. Wolny pass hydration pokazuje się jako koszt runtime, opóźnienie network i problem SSR naraz. Zostawione samo sobie - dostajesz ten sam root problem zgłoszony trzy razy z trzema różnymi ownerami i licznik findings, który kłamie przez zawyżenie.
SSR/hydration to najgorszy przypadek - dzieli styk ze wszystkimi czterema. To ten sam risk podwójnego zgłoszenia, który złapałem budując WCAG toolkit, gdzie focus trap modala i keyboard handler ciągle zgłaszały ten sam finding.
Protokół jest prosty i deterministyczny: collector, który ujawnił finding, decyduje o jego ownerze. Jeśli ujawnił go trace, należy do tego, kto jest ownerem tego sygnału, nie do tego, kto jeszcze mógłby go z sensem zgłosić. Na wierzchu deterministyczny dedup key zwija prawdziwe duplikaty - ten sam check, ten sam plik i linia, albo ta sama metryka i route. Dwóch specjalistów może zauważyć ten sam problem; przeżywa jeden finding, zaatrybuowany raz.
To nie jest efektowne. To różnica między raportem, któremu dev ufa, a raportem, z którym się kłóci.
Czemu nagłówek to split, nie ocena
Część 1 pokazała moje portfolio z oceną C, każdym Core Web Vital na zielono i każdym obszarem ocenionym na A. Ta sprzeczność to nie bug do zaklejenia. To powód, dla którego nagłówek jest zbudowany tak, jak jest.
Performance nie ma jednej osi. Core Web Vitals to prawda użytkownika i biznesu. Lighthouse score to diagnostyka labowa. Oceny obszarów to miejsce, gdzie jest robota. To trzy różne pytania, a pojedyncza litera nie odpowiada uczciwie na żadne - uśrednia je w liczbę, która jest błędna w konkretny, mylący sposób.
Więc nagłówek raportuje dwie osie:
Core Web Vitals: PASS / FAIL (per-metryka good / needs-improvement / poor)
Lighthouse perf: NN/100 (lab, throttled)
Ocena A-F nie została wyrzucona. Zeszła poziom niżej, do ocen per-obszar, gdzie pojedyncza oś faktycznie ma zastosowanie - bo “jak się ma bundle” to pytanie z jedną odpowiedzią.
I jest trzeci stan, który większość narzędzi pomija: unmeasured. Odpal tool tylko na source, bez URL-a do uderzenia, i nie może uczciwie powiedzieć PASS ani FAIL na vitalsach, których nigdy nie zmierzył. Więc mówi unmeasured i oddaje provisional findings-grade z bannerem, który mówi dokładnie to. Tool, który drukuje pewną ocenę dla pomiaru, którego nigdy nie zrobił, to tool, który grzecznie cię okłamuje. Ten odmawia.
Uczciwa rzeczywistość CI
Audyt mediana-z-pięciu z throttlingiem i web-vitals zajmuje jakieś dwie-trzy minuty na URL. To OK dla próbki CI na reprezentatywnej stronie. To też dokładnie powód, dla którego v0.1 audytuje jeden route, nie całą stronę - N routes to N razy tyle, a multi-route to świadomy problem na późniejszą wersję, nie rzecz, którą udawałem, że rozwiązałem teraz.
Jeden realny haczyk wart nazwania dla każdego, kto to sklonuje: Lighthouse znajduje Chrome przez chrome-launcher, który na mojej maszynie wykrył systemowy Chrome. Czysty linuksowy runner CI nie ma systemowego Chrome. Fix to wskazać Lighthouse na Chromium, który Playwright już zainstalował, jakieś 150 MB i już jest. Wolę ci to powiedzieć z góry, niż żebyś wpadł na tym przy pierwszym green-to-red w CI.
Jutro
Część 3: skąd ta metoda naprawdę przyszła. Nie zabawkowa strona - trzy platformy ecommerce, takie z koszykami, krokami płatności i setką routes. Pokażę tool na celowo zepsutym demo, żebyś zobaczył, jak coś znajduje, zamiast nie znajdować nic, przejdę przez uczciwą linię między publicznym distylatem a produkcyjnym zleceniem i wyjaśnię, czemu /perf:fix przeprowadzi cię przez fix, ale nie będzie udawać, że auto-aplikuje te architektoniczne.
#FromTheField