Logo David Burdelak
Webdevelopment

Jak debugować kod JavaScript i PHP krok po kroku

Debugowanie co to

Najwięcej czasu w debugowaniu nie zabiera samo poprawienie błędu, tylko znalezienie miejsca, w którym powstaje. Dlatego zamiast dopisywać kolejne console.log i var_dump na ślepo, pracuję według stałego procesu i z narzędziami, które pozwalają zatrzymać kod w dowolnym momencie i zobaczyć jego stan. W tym wpisie pokazuję, jak debuguję JavaScript w przeglądarce i PHP na serwerze, od odtworzenia błędu po poprawkę, która nie wraca po kolejnym wdrożeniu.

Mój proces krok po kroku

Niezależnie od języka przechodzę przez te same etapy:

KrokCo robięPo co
1. OdtworzenieUstalam dokładne kroki, przeglądarkę, urządzenie, dane i konto, przy których błąd występujeBłędu, którego nie umiem wywołać, nie umiem też potwierdzić jako naprawionego
2. ZawężenieSprawdzam, czy problem jest po stronie przeglądarki, serwera, danych czy konfiguracji, i od kiedy występujeZamiast przeglądać cały projekt, szukam w jednym miejscu
3. HipotezaFormułuję jedno konkretne przypuszczenie, np. „pole przychodzi jako tekst, a nie liczba”Sprawdzam jedną rzecz naraz, a nie zmieniam kilku jednocześnie
4. WeryfikacjaZatrzymuję kod w punkcie przerwania i patrzę na rzeczywiste wartościPotwierdzam przyczynę, zanim cokolwiek poprawię
5. Poprawka i testNaprawiam przyczynę, dopisuję test lub scenariusz, który wcześniej zawodziłTen sam błąd nie wraca po kolejnej zmianie

Przy błędach, które pojawiły się „nagle”, bardzo pomaga historia zmian. Jeśli wiem, że tydzień temu wszystko działało, polecenie git bisect pozwala w kilku krokach znaleźć commit, który wprowadził problem. Dlatego w pracy z Gitem w zespole pilnuję małych, dobrze opisanych commitów, które da się w ten sposób przeszukać.

Debugowanie JavaScriptu w przeglądarce

Podstawowym narzędziem są narzędzia deweloperskie przeglądarki. Z zakładki Sources (Źródła) korzystam znacznie częściej niż z konsoli, bo punkt przerwania pokazuje stan wszystkich zmiennych naraz, a nie tylko tych, które przyszło mi do głowy wypisać.

  • Punkty przerwania na linii – klikam numer linii, przeładowuję stronę i krok po kroku przechodzę przez kod, obserwując zmienne i stos wywołań.
  • Warunkowe punkty przerwania – kod zatrzymuje się tylko wtedy, gdy spełniony jest warunek, np. item.price === undefined. Niezastąpione przy pętlach po setkach elementów.
  • Logpointy – wypisują wartość w konsoli bez zatrzymywania kodu i bez zmieniania plików projektu.
  • Punkty przerwania na zdarzeniach i żądaniach – zatrzymanie przy kliknięciu, zmianie w DOM czy wysłaniu żądania fetch. Pozwalają znaleźć kod, o którym nie wiem, gdzie jest.
  • Zakładka Network (Sieć) – przy błędach komunikacji z serwerem najpierw sprawdzam, co faktycznie zostało wysłane i co wróciło, a dopiero potem szukam błędu w kodzie.

Na produkcji kod jest zwykle zminifikowany, więc włączam mapy źródeł, żeby błędy wskazywały prawdziwe pliki i linie. W samym kodzie przydaje się instrukcja debugger;, która zatrzymuje wykonanie, gdy narzędzia deweloperskie są otwarte, oraz console.table() do czytelnego podglądu tablic obiektów.

Osobną kategorią są problemy z płynnością: zacinające się przewijanie czy opóźnione reakcje na kliknięcia. Tu zamiast punktów przerwania używam zakładki Performance, która pokazuje długie zadania blokujące przeglądarkę. Mechanizm stojący za takimi problemami to event loop i kolejka zadań, a ich rozwiązanie zwykle sprowadza się do rozbijania ciężkiego kodu na mniejsze części.

Debugowanie PHP na serwerze

W PHP zaczynam od logów. Na środowisku produkcyjnym błędy nigdy nie powinny wyświetlać się użytkownikom, ale zawsze powinny trafiać do pliku:

; php.ini na produkcji
display_errors = Off
log_errors = On
error_log = /sciezka/do/logow/php-error.log

W WordPressie ten sam efekt daje ustawienie w wp-config.php stałych WP_DEBUG i WP_DEBUG_LOG na true oraz WP_DEBUG_DISPLAY na false. Biała strona albo błąd 500 prawie zawsze mają swoją przyczynę zapisaną w logu, wystarczy do niego zajrzeć.

Do bardziej złożonych problemów używam Xdebuga, który daje w PHP to samo, co narzędzia deweloperskie w JavaScripcie: punkty przerwania, podgląd zmiennych i przechodzenie przez kod linia po linii z poziomu edytora, np. VS Code czy PhpStorm. Lokalnie uruchamiam go w kontenerze razem z resztą środowiska, bo po przejściu z XAMPP-a na Dockera konfiguracja Xdebuga sprowadza się do kilku linii:

xdebug.mode = debug
xdebug.start_with_request = trigger
xdebug.client_host = host.docker.internal

Tryb trigger uruchamia debugowanie tylko dla wybranych żądań, dzięki czemu reszta strony nie zwalnia. Xdebuga nigdy nie włączam na serwerze produkcyjnym.

Przykład z praktyki

Typowy przypadek ze sklepu: suma zamówienia czasem wyświetla się jako absurdalnie duża liczba. Zamiast zgadywać, ustawiam warunkowy punkt przerwania w funkcji liczącej sumę i zatrzymuję kod tylko wtedy, gdy koszt dostawy nie jest liczbą:

function orderTotal(items, shipping) {
    let total = 0;
    for (const item of items) {
        total += item.price * item.qty;
    }
    return total + shipping;   // warunek przerwania: typeof shipping !== 'number'
}

Kod zatrzymuje się, gdy koszt dostawy przychodzi z API jako tekst "15.00". Mnożenie w pętli działa poprawnie, bo JavaScript zamienia tekst na liczbę, ale dodawanie z tekstem skleja wartości, więc z 39.98 i „15.00” powstaje „39.9815.00”. Poprawka nie polega na dopisaniu Number() w tej jednej linii, tylko na ujednoliceniu typów w miejscu, w którym dane przychodzą z API, i dopisaniu testu dla wartości w formie tekstu. Niejawna konwersja typów to jeden z najczęstszych błędów JavaScript, jakie spotykam.

Błędy, które występują tylko na produkcji

Najtrudniejsze są błędy, których nie da się odtworzyć lokalnie. Wtedy pracuję na danych, które zbiera sama aplikacja:

  • Logi z kontekstem – oprócz komunikatu błędu zapisuję adres, identyfikator żądania, wersję aplikacji i kluczowe parametry, bez danych osobowych. Regularna analiza logów serwera pokazuje też, czy błąd dotyczy jednego adresu, czy całej sekcji.
  • Zbieranie błędów z przeglądarek – narzędzie do monitorowania błędów JavaScript pokazuje, na jakich przeglądarkach i stronach występuje problem, razem ze stosem wywołań.
  • Różnice między środowiskami – inna wersja PHP, inne rozszerzenia, pamięć podręczna, CDN, inne dane w bazie. Gdy błąd występuje tylko na produkcji, porównanie konfiguracji często daje odpowiedź szybciej niż czytanie kodu.
  • Środowisko testowe z kopią danych – odtwarzam problem na stagingu z danymi zbliżonymi do produkcyjnych, oczywiście po zanonimizowaniu danych klientów.

Debugowanie z pomocą AI

Coraz częściej wklejam stos wywołań i fragment kodu do Claude'a, żeby szybciej postawić hipotezę. Sprawdza się to szczególnie przy nieczytelnych komunikatach błędów i w kodzie, którego nie pisałem. Trzymam się jednak dwóch zasad: nie przekazuję haseł, kluczy ani danych osobowych z logów, a każdą zaproponowaną przyczynę weryfikuję punktem przerwania, zanim zmienię kod. AI świetnie podsuwa, gdzie szukać, ale dopiero rzeczywiste wartości zmiennych potwierdzają, że to właśnie tam leży problem.