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:
| Krok | Co robię | Po co |
|---|---|---|
| 1. Odtworzenie | Ustalam dokładne kroki, przeglądarkę, urządzenie, dane i konto, przy których błąd występuje | Błędu, którego nie umiem wywołać, nie umiem też potwierdzić jako naprawionego |
| 2. Zawężenie | Sprawdzam, czy problem jest po stronie przeglądarki, serwera, danych czy konfiguracji, i od kiedy występuje | Zamiast przeglądać cały projekt, szukam w jednym miejscu |
| 3. Hipoteza | Formułuję jedno konkretne przypuszczenie, np. „pole przychodzi jako tekst, a nie liczba” | Sprawdzam jedną rzecz naraz, a nie zmieniam kilku jednocześnie |
| 4. Weryfikacja | Zatrzymuję kod w punkcie przerwania i patrzę na rzeczywiste wartości | Potwierdzam przyczynę, zanim cokolwiek poprawię |
| 5. Poprawka i test | Naprawiam 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.