Kodowanie z myślą o wydajności

Z wydajnością w projektach WordPressowych jest jakoś tak, że niby wszyscy wiedzą, że jest ważna, ale trafia ona na listę TODO pod sam koniec projektu. Co więcej, nader często „optymalizacja” spowadza się do… instalacji „jakiejś wtyczki do cache’u”.

Cache nie rozwiązuje problemów

Niestety – świat nie jest tak prosty, WordPress nie jest tak prosty, a cache to tylko plasterek na większą ranę lub puder na niedoskonałości kodu. Plasterek, który często nie działa. Nie cała treść trafia bowiem do cache’u, zwłaszcza jeśli mówimy o użytkownikach zalogowanych czy funkcjonujących na pograniczu dynamicznych funkcjonalności. Cache jest problematyczny w przypadku e-commerce. Cache nierzadko dezorientuje użytkowników („bo dodałem post, a ludzie piszą, że go nie widzą”). Cache potrafi „zepsuć” style lub skrypty. No i last but not least, najsoczystsze kąski w konfiguracji wtyczek cache’ujących zazwyczaj umieszczone są w zakładce Premium… Musimy sobie zatem uświadomić już na etapie programowania strony, że każde niedociągnięcie po stronie backendu wcześniej czy później zostanie nam policzone. Cache to tylko narzędzie, a nie rozwiązanie.

Diabeł tkwi w szczegółach, a szczegóły to często kwestia skali

Kluczową rzeczą przy kodowaniu pod WordPressa jest to, żeby rozumieć, jak działają używane przez nas funkcje i jak skaluje się napisany przez nas kod. Bardzo często napotykam projekty, w których funkcje lub metody wywoływane są nadmiarowo, umieszczane są w pętlach, są zagnieżdżane i choć generalnie robią to, do czego zostały napisane, to jednak robią to angażując o wiele za dużo mocy obliczeniowej, niż powinny. Prosty przykład – pobieranie customowej listy postów przy użyciu funkcji get_posts(). Zwraca ona, standardowo, tablicę pełnych obiektów WP_Post – z całą strukturą i wszystkimi polami. A przecież czasami potrzebujemy wyłącznie identyfikatorów wpisu do dalszego użycia. Jeden drobny parametr potrafi znacząco przyspieszyć wykonywanie kodu:

Już ten trywialny przykład pokazuje, jak niewiele czasami potrzeba, by ograniczyć ilość mocy obliczeniowej do wykonania danego zadania. Podobnie z innymi funkcjami: gdy nie potrzebujesz np. meta danych, niech aplikacja ich nie pobiera. Jeżeli nie potrzebujesz przeszukiwać wszystkich postów, ogranicz liczbę zwracanych wyników do niezbędnego minimum. Nawet pojedynczy nieprzemyślany, zapętlony SELECT może przy dużej liczbie rekordów zamienić Twoją stronę w generator timeoutów, szczególnie na słabszych hostingach.

Pierwszy raz w produkcji? Skaluj swój kod z głową

Wydajność bywa jeszcze bardziej zdradliwa, gdy rozmiar projektu zaczyna rosnąć. To, co działa płynnie na testowej bazie z setką rekordów, może całkowicie odmówić współpracy, gdy w grę wchodzi dziesięć tysięcy postów. Każda, nawet niewinna pętla czy podwójne pobranie danych z bazy, przy tak dużej liczbie wpisów wyraźnie wydłuża czas odpowiedzi, co może prowadzić nie tylko do niewygody użytkowników, ale do „zabicia” serwera – błąd, za który czasem automatycznie winimy hosting, może leżeć całkowicie po naszej stronie. WordPress jest złudnie prosty dla początkującego programisty, co szybko może stać się przekleństwem, jeśli nie patrzymy na wydajność na każdym kroku. Doświadczeni developerzy widzieli więcej niż raz, jak dobrze zapowiadająca się strona w – dajmy na to – Black Friday zupełnie przestaje działać.

TTFB i rzeczywista wydajność

Wydajność strony w znacznym stopniu zależy od parametru TTFB (Time To First Byte). Możesz mieć perfekcyjnie zoptymalizowane assety, świetną kompresję obrazów, CDN i reguły cache’owania, ale jeśli Twój PHP będzie mielił zapytania przez 2 sekundy, użytkownik na frontendzie po prostu to zauważy. Co z tego, że zasoby przychodzą szybciej, jeśli serwer przez niemal dwie sekundy nie jest w stanie wygenerować pierwszego bajta? Cache – nawet najcudowniejszy – nie pomoże, jeżeli request omija go i wymaga PHP. Tylko sprawny, zoptymalizowany kod daje gwarancję, że strona zareaguje szybko na każde nowe żądanie.

Kodowanie z myślą o wydajności – garść rad i spostrzeżeń

Kluczowe praktyki podnoszenia wydajności w WordPressie to nic inne, jak mikro-optymalizacje sumujące się na makro różnicę. Kilka absolutnych fundamentów:

  • Czytaj dokumentację WP. Zaskakująca liczba funkcji ma parametry pozwalające na ogromne oszczędności.
  • Analizuj, co masz w zmiennych. Nie dubluj wywołań. Jeśli masz już dane, nie pobieraj ich drugi raz z bazy.
  • Tam, gdzie możesz zapamiętać coś na chwilę bez ryzyka desynchronizacji danych lub zaserwowania nieaktualnego contentu – używaj API transientów.
  • Monitoring to Twój przyjaciel. Query Monitor jest tu nieoceniony, pozwala dogłębnie przeanalizować czas zapytań, stack trace’y i decyzje, które mogą uratować projekt przed katastrofą.
  • Jeśli chcesz mieć pełną kontrolę, sprawdzaj czasy wykonania kodu. Query Monitor udostępnia akcje, których możemy użyć we własnych implementacjach, aby potem w panelu Timings móc zobaczyć, ile milisekund (lub sekund!) zajmuje ich wykonanie:
  • Jeśli nie chcesz używać Query Monitora, możesz np. logować czasy wykonania do debug.log przy użyciu własnego kodu:

Dzięki takiemu podejściu niemal od razu zorientujesz się, które fragmenty są wąskim gardłem.

(Przy okazji mała podpowiedź: Query Monitor jest znakomity podczas developmentu, ale pamiętaj wyłączyć tę wtyczkę na produkcji – będzie obciążać stronę, szczególnie dla zalogowanych użytkowników).

Efekt końcowy – mniej znaczy szybciej

Zakończmy cytatem, będącym maksymą przewodnią całego wordpressowego projektu: „Code is poetry.” Samo hasło jest piękne, w praktyce jednak czasami zamiast haiku zdarza nam się pisać epopeje – struktury przesadne i nieefektywne. Tymczasem im mniej, tym lepiej. Pisząc kod do nowych funkcjonalności, pamiętaj: minimalizm w zdecydowanej większości przypadków wygrywa ze skomplikowaniem. Proste funkcje o czytelnych zadaniach, bez zbędnych pętli i dublowania logiki – to one gwarantują zarówno skalowalność, jak i przyszłą odporność na wzrost ruchu.

Wydajność to nie luksus na koniec projektu; to mindset obecny od pierwszej linijki kodu.


Fot. Sebastian Kaulitzki/Adobe Stock