Rzemiosło · The Craft
Czym jest The CraftPoziomyJak zacząćRozdziały Szukaj Pobierz
Level 3 · Kodeks ~3 min czytania

W jednym zdaniu: Najpierw mierz, potem optymalizuj: indeksy, koniec z N+1, streaming — szybkość udowodniona liczbami.

13 — Wydajność: frontend i SQL

Przykazanie III w czystej postaci: weryfikuj, nie deklaruj — zastosowane do szybkości. „Wydaje się szybciej” nie istnieje. Istnieją liczby przed/po.

Wydajność to nie przeczucie, to pomiar. Najczęstszy błąd optymalizacji: zgadywanie, co jest wolne, i „poprawianie” tego bez dowodu, że było problemem. Mierz najpierw, popraw to, co metryka wskazuje, i udowodnij liczbą, że poprawiłeś. Wydajność to też SEO (Core Web Vitals — Miary od Google: jak szybko strona się pokazuje, jak stabilnie się układa i jak szybko reaguje. Wpływają na pozycję w Google i na to, czy użytkownik nie ucieknie. Mierzalne, więc da się je poprawiać.10) i koszt (szybsze zapytanie = tańszy serwer → 12).

Mierz najpierw

  • Lighthouse / Core Web Vitals: LCP (largest contentful paint), CLS (layout shift), INP (interaction), TBT. To są twarde liczby, nie wrażenia.
  • Przed/po, nie „wydaje się szybciej”. Np. optymalizacja home: preload hero, GPU-promoted animacje, content-visibility: auto poniżej folda; raportujesz LCP przed i po, nie „chyba lżej”. → 03
  • SQL — Język zapytań do bazy danych — sposób, w jaki „pytasz” bazę o dane albo je zmieniasz. Uniwersalny standard rozmowy z bazą. Źle napisane zapytanie potrafi zamulić całą aplikację.: EXPLAIN QUERY PLAN mówi, czy zapytanie używa indeksu, czy skanuje całą tabelę.

Frontend

  • Code-splitting + lazy-load below-fold — nie ładuj tego, czego user nie widzi.
  • Obrazy: WebP (np. konwersja wszystkich do WebP q=95), responsive srcset, preload hero (LCP), reszta lazy.
  • Fonty: self-host (np. Playfair/Outfit, jak na jakub.solutions), font-display: swap — tekst widoczny zanim font dojdzie.
  • content-visibility: auto na sekcjach poniżej folda — przeglądarka pomija render niewidocznego.
  • GPU-promoted animacje (translateZ(0)/transform) zamiast layoutujących właściwości.
  • Cache — Tymczasowo zapamiętany wynik, żeby nie liczyć tego samego od nowa przy każdym pytaniu. Przyspiesza aplikację, ale bywa pułapką: nieświeży cache pokazuje stare dane. statyków: immutable + długi max-age na prodzie (np. 7 dni immutable prod).
  • Cache-busting per deploy: skoro CSS/JS są immutable, doklej ?v=<git-short-hash> do każdego linku — każdy deploy zmienia URL → świeży pobór. Bez tego user widzi starą apkę (rozjechany layout) aż do twardego odświeżenia (→ 05).
  • Streaming — Wysyłanie dużej odpowiedzi kawałkami, na bieżąco, zamiast ładowania wszystkiego naraz do pamięci. Aplikacja nie zatyka się przy wielkich danych — użytkownik widzi wynik szybciej. / SSE dla czatu — odpowiedź LLM token-by-token (np. przez SSE), user widzi pierwsze słowa od razu, a nie pustkę do końca generacji. → 08
  • Server-side pagination — nigdy nie ślij całego katalogu do przeglądarki (np. paginacja po stronie serwera dla ~kilku tysięcy pozycji).

SQL

  • Indeks (bazodanowy) — Skorowidz w bazie danych, dzięki któremu wyszukiwanie idzie błyskawicznie, zamiast przeglądać wszystko po kolei. Pierwszy ruch przy wolnych zapytaniach — jak indeks na końcu książki zamiast czytania 400 stron. na kolumnach z WHERE i JOIN — gorące zapytanie bez indeksu to skan całej tabeli.
  • Partial indexes — np. uniq_prices_active … WHERE expired_at IS NULL (indeks tylko na aktywnych cenach — mniejszy, szybszy, wymusza unikalność, → 11).
  • EXPLAIN QUERY PLAN przed i po dodaniu indeksu — dowód, że plan się zmienił.
  • Unikaj N+1 — Błąd wydajności: aplikacja odpytuje bazę 200 razy zamiast raz, bo pyta osobno o każdy element. Najczęstsza przyczyna „mulącej” listy. Jak 200 telefonów zamiast jednej prośby o całą listę. — nie zapytanie w pętli po każdym wierszu; batch/join za jednym razem.
  • SELECT tylko potrzebnych kolumn — nie SELECT *, gdy potrzebujesz trzech pól.
  • Paginacja server-side + cache’owane agregaty (np. site_stats zamiast COUNT(*) po całej bazie na każde wejście na home).
  • WAL — Write-Ahead Log: tryb bazy, w którym zmiany najpierw trafiają do dziennika, a potem do danych. Daje bezpieczeństwo (odtwarzalność po awarii) i lepszą równoczesność odczytu/zapisu. (SQLite — Najprostsza baza danych — cała mieszka w jednym pliku, bez osobnego serwera. Świetny default na start: zero konfiguracji, łatwy backup (kopiujesz plik). Przy wzroście migrujesz wyżej.) — czytelnicy nie blokują pisarza; domyślny tryb w projekcie referencyjnym.
  • Ostrożnie z LIKE '%foo' — wiodący wildcard zabija indeks (full scan); rozważ FTS, jeśli to gorąca ścieżka wyszukiwania.

Anty-wzorce

  • 🚫 Optymalizacja bez pomiaru — „poprawiłem”, nie wiedząc, czy było wolne (→ 03).
  • 🚫 Brak indeksu na gorącym zapytaniu — najczęstsza przyczyna wolnej strony katalogu.
  • 🚫 N+1 w pętli — 200 zapytań tam, gdzie wystarczył jeden join.
  • 🚫 SELECT * — przesyłasz i deserializujesz kolumny, których nie używasz.
  • 🚫 Client-side pagination ogromnych zbiorów — ślesz kilka tysięcy rekordów, by pokazać 20.
  • 🚫 Blokowanie UI w oczekiwaniu na pełną odpowiedź LLM zamiast streamingu (→ 12).
  • 🚫 Brak cache drogich agregatówCOUNT(*) po całej bazie na każde żądanie.

Dla nowych projektów

Wpisz do Dnia 0 (→ 07): baseline Lighthouse zaraz po pierwszej działającej stronie (masz punkt odniesienia), indeksy na kolumnach filtrów od pierwszej Migracja (bazy danych) — Kontrolowana zmiana układu bazy — dodanie kolumny, tabeli czy przeniesienie danych — krok po kroku. Jak remont według planu: przebudowa danych w ustalonej kolejności, żeby nic się nie „zawaliło”., EXPLAIN QUERY PLAN jako nawyk przy każdym gorącym zapytaniu. Reguła nadrzędna: żadna optymalizacja bez liczby przed i po — bo bez dowodu „optymalizacja” bywa regresją w przebraniu (→ 03). Szybkość to jednocześnie SEO (→ 10) i koszt infrastruktury (→ 12) — jedna inwestycja, trzy zwroty.

Treść doktryny prowadzimy po polsku.