Dlaczego kod z AI łamie architekturę WordPressa i jak to naprawić na poziomie procesu

Kilka miesięcy temu dostałem telefon z prośbą o audyt projektu e-commerce na WordPressie. Zespół był w kryzysie – kod „działał”, ale każda zmiana wymagała godzin debugowania, a klient nie mógł samodzielnie zaktualizować nawet prostego tekstu na stronie. Największy problem? Większość kodu została wygenerowana przez AI i nikt nie zauważył, że omija ona całą architekturę WordPressa.

To nie był odosobniony przypadek. W ciągu ostatniego roku widziałem dziesiątki projektów WordPress, gdzie zespoły używające Copilota, Cursora czy ChatGPT otrzymywały kod, który kompilował się bezbłędnie i nawet przechodził podstawowe testy – ale fundamentalnie łamał zasady działania CMS-a. Problem nie leżał w samym AI. Leżał w braku zrozumienia, że „działa” i „jest dobrze zaprojektowane” to dwie różne rzeczy.

Jak AI generuje kod dla WordPressa i dlaczego to problem

Weźmy prosty przykład. Dostałeś task: dodaj formularz kontaktowy z custom validacją i zapisem do bazy. Wklejasz to do AI, dostajesz 200 linii PHP, które robią dokładnie to, o co prosiłeś. Formularz wysyła dane, walidacja działa, wszystko ląduje w bazie. Merge, deploy, done.

Problem ujawnia się trzy tygodnie później. Klient chce zmienić tekst przycisku. Ale tekst jest zahardcodowany w funkcji PHP. Dobra, można to szybko poprawić. Tydzień później klient chce dodać pole do formularza. Okazuje się, że logika walidacji jest monolityczna – nie ma hooków, nie ma filtrów, musisz edytować surowy kod. Kolejny tydzień – formularz ma być dostępny na trzech różnych stronach, ale jest bezpośrednio wpisany w template file. Zaczynasz kopiować kod albo przepisywać całość.

Ten scenariusz powtarza się w nieskończoność, bo AI generuje kod proceduralny, który rozwiązuje konkretny problem tu i teraz. Nie projektuje architektury, która będzie ewoluować przez następne dwa lata. Dla AI formularz to funkcja, która przyjmuje POST, waliduje dane i zapisuje je do tabeli. Dla WordPressa formularz to shortcode albo Gutenberg block z hookami pozwalającymi na rozszerzanie, filtrami modyfikującymi dane, Custom Post Type przechowującym wpisy i REST API endpoint dla integracji.

Największy problem z kodem generowanym przez AI nie jest techniczny. Jest poznawczy. Zespół patrzy na działający kod i nie widzi tego, czego tam nie ma – abstrakcji, punktów rozszerzalności, separacji warstw. Widzą formularz, który działa, więc zakładają, że został dobrze zaprojektowany.

Trzy warstwy architektury które AI rutynowo łamie

WordPress ma bardzo konkretną filozofię architektoniczną opartą o separację contentu, logiki i prezentacji przez system pluginów, motywów i hooków. AI tej filozofii nie rozumie, bo trenowało się na milionach linii kodu z różnych kontekstów – od czystego PHP przez Laravela po custom frameworks. Dla AI WordPress to po prostu PHP z jakimiś funkcjami helper.

Warstwa prezentacji: hardcoding vs pluggable content

Typowy kod wygenerowany przez AI dla sekcji z produktami wygląda mniej więcej tak:

get_results("SELECT * FROM custom_products ORDER BY price");

echo "
"; foreach ($products as $product) { echo "
"; echo "<*h3>" . $product->name . ""; echo "

Price: $" . $product->price . "

"; echo "id . ")">Add to Cart"; echo "
"; } echo "
"; echo ""; } ?>

Ten kod działa. Wyświetla produkty, dodaje je do koszyka. Ale tworzy kilka fundamentalnych problemów. Po pierwsze, cała prezentacja jest zahardcodowana w PHP. Jeśli klient chce zmienić layout, musi edytować kod. Po drugie, JavaScript jest inline, więc nie przejdzie przez pipeline cache”owania. Po trzecie, bezpośrednie wywołanie custom PHP file dla AJAX omija całą WordPress security layer – brak nonce verification, brak capability checks.

WordPress-way dla tego samego problemu to Custom Post Type dla produktów, shortcode albo Gutenberg block dla wyświetlania z template file który można nadpisać w child theme, wp_enqueue_script dla JavaScript z wp_localize_script przekazującym AJAX URL, i admin-ajax.php albo REST API endpoint z proper authentication. Brzmi jak więcej pracy, ale daje ci pluggable architecture – klient może zmienić template bez dotykania kodu, możesz cache”ować JS na CDN, inne pluginy mogą modyfikować listę produktów przez filtry.

Problem w tym, że jeśli promptujesz AI „napisz kod do wyświetlania produktów”, dostaniesz pierwszą wersję. Żeby dostać drugą, musisz explicite wymusić „użyj Custom Post Type, shortcode z parametrami, enqueue scripts properly, wykorzystaj WordPress AJAX API”. I musisz wiedzieć, że to wymuszać, co wymaga znajomości architektury WordPressa lepszej niż przeciętny developer który polega na AI.

Warstwa logiki: direct calls vs hooks system

Drugi powracający problem to całkowite omijanie systemu hooków. AI generuje kod, który robi rzeczy bezpośrednio, zamiast dawać możliwość ingerencji przez actions i filters.

Widziałem projekt gdzie formularz rejestracyjny został wygenerowany przez AI jako monolityczna funkcja procesująca POST. Walidacja email, zapis do bazy, wysłanie maila powitalnego – wszystko w jednej funkcji 150 linii. Działa świetnie dopóki nie potrzebujesz dodać custom logic – na przykład integracji z CRM, która ma być uruchamiana po udanej rejestracji. Nie masz hooka, więc musisz edytować oryginalną funkcję. Dodajesz swój kod, ale miesiąc później ktoś inny musi dodać kolejną integrację i edytuje tę samą funkcję. Po pół roku masz spaghetti code, którego nikt nie jest w stanie prześledzić.

WordPress ma do tego do_action() i apply_filters(). Rejestracja użytkownika powinna wyzwalać action „user_registered” i każdy plugin może się do niego podpiąć bez edytowania core logic. Adres email powinien przechodzić przez filter „registration_email” pozwalający na modyfikację. To nie jest academic best practice, to konkretny mechanizm który sprawia, że WordPress jest rozszerzalny bez fork”owania kodu.

AI tego nie generuje, bo nie widzi potrzeby. Rozwiązuje problem który mu podałeś, nie projektuje pod przyszłe rozszerzenia. I to jest kluczowa różnica między kodem który działa a kodem który można utrzymywać.

Warstwa danych: custom tables vs CPT architecture

Trzeci klasyczny problem – AI uwielbia tworzyć custom tabele w bazie. Projektujesz sklep? AI zaproponuje ci „products”, „orders”, „customers”. Ma sens z perspektywy czystego SQL. Ma zero sensu w kontekście WordPressa.

WordPress ma wp_posts i meta tables z bardzo konkretnego powodu – pluggable content model. Jeśli produkty są Custom Post Type, automatycznie dostajesz revisions, autosave, trash, search integration, REST API endpoints, role-based permissions. Jeśli są w custom table, musisz to wszystko zaimplementować sam. Albo – co widziałem częściej – rezygnujesz z tych funkcji, bo „na razie nie są potrzebne”.

Problem ujawnia się rok później kiedy klient chce żeby produkty były przeszukiwalne przez główną wyszukiwarkę WordPressa. Albo kiedy chce mieć drafty produktów. Albo kiedy chce integrację z pluginem SEO który zakłada, że wszystko jest w wp_posts. Nagle okazuje się, że custom table to vendor lock-in na twój custom kod i żadne narzędzie ekosystemu WordPressa nie będzie działać bez dodatkowych adapterów.

Był moment w projekcie e-commerce kiedy klient wyraźnie powiedział „potrzebujemy custom tabeli dla zamówień bo WooCommerce jest za ciężki”. Brzmiało rozsądnie – WooCommerce rzeczywiście ma swoje problemy z performance na dużą skalę. Problem w tym, że „custom tabela” oznaczała też custom panel admina, custom reporting, custom export do CSV, custom integrację z każdą bramką płatności. Po trzech miesiącach development zastąpiliśmy to WooCommerce z wyłączonymi niepotrzebnymi modułami i performance był akceptowalny, a zaoszczędziliśmy dziesiątki godzin maintenance.

To nie była porażka klienta. To była lekcja, że czasem uproszczone wymaganie („nie używaj WooCommerce”) kryje faktyczny problem („ostatnia wersja była wolna”) i rozwiązanie wymaga doprecyzowania rzeczywistego pain point zamiast ślepego wykonania polecenia. Zespół który nie pyta „dlaczego” tylko implementuje „co” buduje niewłaściwy system, nawet jeśli kod jest technicznie poprawny.

"AI bardzo lubi pisać kod tak, jakby każda funkcja miała być ostatnią w historii projektu. Produkcja zaskakująco rzadko podziela ten optymizm."

Proces code review dla kodu generowanego przez AI

Realnym rozwiązaniem nie jest porzucenie AI. Jest za bardzo produktywne jako narzędzie do boilerplate”u. Rozwiązaniem jest traktowanie kodu z AI tak, jak traktujesz kod juniora – działający, ale wymagający review pod kątem architektury.

Najgorsze co możesz zrobić to merge bez review bo „to tylko AI, sprawdziłem że działa”. W projekcie który auditowałem było 40+ commitów z kodem AI bez review. Rezultat – każdy fragment używał innego pattern. Jedna integracja płatności miała logikę w theme functions.php, inna w pluginie, trzecia bezpośrednio w template. Żadnej spójności, bo każdy developer promptował AI osobno i mergował co dostał.

Code review dla AI-generated kodu wymaga innych pytań niż standardowy review. Nie pytasz „czy to działa” – zakładasz że działa. Pytasz „czy to pasuje do architektury”, „czy to jest extensible”, „czy za rok nie będę musiał tego przepisywać”. Konkretnie – czy używa hooków, czy teksty są w translation functions, czy assets są properly enqueued, czy dane idą przez standard APIs.

W praktyce zrobiłem sobie mental checklist który przechodzę przy każdym review kodu WordPress:

Czy prezentacja jest oddzielona od logiki? Jeśli widzę echo HTML w funkcji biznesowej, red flag. Czy są punkty rozszerzalności? Jeśli funkcja robi pięć rzeczy i nie ma ani jednego do_action(), red flag. Czy używa WordPress APIs? Jeśli widzę curl do zewnętrznego API zamiast wp_remote_get(), red flag. Czy teksty są hardcoded? Jeśli nie ma __() ani _e(), red flag.

To nie jest pedanteria. To różnica między kodem który możesz utrzymywać a kodem który za pół roku wymaga rewrite.

Automatyzacja pomaga, ale tylko częściowo. PHPCS z WordPress Coding Standards złapie oczywiste problemy – hardcoded URLs, brak sanitization, deprecated functions. Ale nie złapie architektury. Nie powiedział ci że powinienieś był użyć Custom Post Type zamiast custom table. Nie powiedział ci że brakuje hooków. Musisz tego nauczyć ludzi.

W projekcie który naprawiałem wprowadziłem prosty flow – każdy kod z AI idzie do osobnego branch z prefiksem „ai/”. Wymaga approval od senior dev który zna WordPress architecture. W PR description musisz napisać nie tylko co kod robi, ale dlaczego wybrałeś takie podejście i gdzie są punkty rozszerzalności. Brzmi jak overhead, ale eliminuje 90% problemów zanim trafią do maina.

Drugi element to architecture reference doc – żywy dokument opisujący jak robimy konkretne rzeczy w projekcie. Nie generyczna dokumentacja „jak używać hooków”, ale konkretne decyzje – „custom content types implementujemy jako CPT z ACF fields”, „wszystkie AJAX calls idą przez admin-ajax.php z action prefix „project_””, „teksty user-facing zawsze przez translation functions nawet jeśli nie planujemy tłumaczeń”. Kiedy dostajesz kod z AI, pierwsza rzecz to sprawdzenie czy pasuje do reference. Jeśli nie, albo refactor, albo update reference jeśli masz dobry powód.

Refactoring istniejącego AI-generated kodu

Gorzej jeśli masz już kilkadziesiąt commitów kodu który łamie architekturę i projekt jest w produkcji. Przepisywanie wszystkiego nie jest opcją – zbyt ryzykowne i zbyt drogie. Musisz refactorować inkrementalnie, priorytetyzując fragmenty które faktycznie sprawiają ból.

Zacznij od identyfikacji największych problemów. W projekcie który naprawiałem pierwszym symptomem były ciągłe konflikty przy mergeach. Okazało się, że większość logiki była w functions.php motywu – jednym gigantycznym pliku edytowanym przez każdego. To był oczywisty target – wydzielić custom functions do osobnego pluginu z podzielonymi plikami według funkcjonalności.

Drugi symptom – nie można było włączyć żadnego caching pluginu, bo strona się sypała. Przyczyna – inline JavaScript generowany przez AI bezpośrednio w PHP templates, pełen zmiennych które były różne dla każdego użytkownika. Nie dało się tego cache”ować. Refactor – przenieść wszystkie scripts do properly enqueued files, przekazywać dane przez wp_localize_script, zaimplementować fragment caching dla dynamicznych części.

Trzeci symptom – każda zmiana tekstu przez klienta wymagała ticketa dla developera. Powód – teksty hardcoded w kodzie PHP. Refactor – wydzielić wszystkie user-facing strings do ACF options page albo do pluginu z manageable settings.

Kluczowa zasada przy refactoringu – nie zmieniaj behavior, tylko struktur. Jeśli coś działa dla użytkownika końcowego, niech działa identycznie po refactorze. Zmienia się tylko kod pod spodem. To pozwala na incremental rollout i łatwe rollback jeśli coś pójdzie nie tak.

Pisałem testy regresyjne dla każdego refactored fragmentu, ale nie unit testy – WordPress jest zbyt coupled do bazy żeby sensownie unit testować. Zamiast tego – end-to-end testy sprawdzające user flows. Formularz wysyła dane? Sprawdź że po refactorze dalej wysyła i lądują w tym samym miejscu. Produkt dodaje się do koszyka? Sprawdź że mechanizm działa tak samo. To pozwala na agresywniejszy refactoring z mniejszym ryzykiem.

Problem z refactoringiem kodu AI jest taki, że często nikt nie rozumie dlaczego kod został napisany w konkretny sposób. Normalnie pytasz autora „czemu to tak zrobiłeś”, dostajesz context, refactorujesz świadomie. Z AI nie masz kogo zapytać. Musisz reverse-engineer intent z samego kodu, co jest znacznie trudniejsze.

Dlatego przy refactoringu zostawiałem komentarze nie tylko co zmieniłem, ale dlaczego poprzednie podejście było problematyczne. „Replaced direct SQL query with WP_Query to enable post caching and filter hooks”. „Moved inline JS to enqueued file to allow CDN caching”. „Changed custom table to CPT to integrate with WordPress search”. To nie jest dla maszyny, to jest dla następnego developera który będzie to czytał za pół roku i zastanawiał się czemu tak nie zostawić jak było.

Jak instruować AI żeby generowało lepszy kod WordPress

Najbardziej efektywne rozwiązanie to nie naprawianie kodu po fakcie, tylko zmiana jak promptujesz AI. Problem w tym, że większość ludzi traktuje AI jak Google – wpisujesz co chcesz, dostajesz odpowiedź. Z generowaniem kodu to nie działa. Musisz dostarczyć context.

Zły prompt: „Napisz funkcję do zapisywania formularza kontaktowego w WordPress”.

Dobry prompt: „Napisz WordPress plugin który rejestruje shortcode do formularza kontaktowego. Użyj wp_nonce dla security, sanitizuj input przez sanitize_text_field(), zapisz dane jako Custom Post Type „contact_submission” z meta fields zamiast custom tabeli, wyślij email przez wp_mail(), wszystkie teksty przez translation functions, dodaj action hook po zapisaniu danych. Załóż że będą potrzebne customizacje validation logic przez innych developerów.”

Różnica jest fundamentalna. Pierwszy prompt dostaniesz monolityczną funkcję z hardcoded wszystkim. Drugi dostajesz pluggable architecture zgodną z WordPress conventions.

Jeszcze lepiej jeśli masz architecture reference doc i możesz go wkleić jako context. „Oto nasze conventions dla projektu [paste doc]. Napisz funkcję X zgodną z tymi zasadami.” AI jest zaskakująco dobre w stosowaniu się do podanych conventions jeśli są explicit.

Problem w tym, że to wymaga żeby osoba promptująca rozumiała architekturę WordPressa wystarczająco dobrze, żeby wiedzieć czego żądać. Junior developer który nie zna hooków nie wie, że powinien o nie poprosić. Dostaje kod bez hooków, wygląda rozsądnie, merguje. Dlatego AI jako narzędzie dla juniorów jest szczególnie niebezpieczne – nie mają wiedzy żeby ocenić jakość wygenerowanego kodu.

Rozwiązanie które działa – pair programming gdzie senior promptuje AI albo przynajmniej review promptu przed generowaniem. „Zamierzasz promptować AI żeby napisało X, pokaż mi prompt zanim to uruchomisz”. Brzmi jak overhead, ale w praktyce senior może w 30 sekund dodać dwa zdania do promptu które zmienią wygenerowany kod z „do wyrzucenia” na „dobry starting point”.

Drugi approach – pre-written prompt templates dla typowych tasków. Nie „napisz formularz”, tylko „użyj template FORM_WITH_CPT i dostosuj dla pól X, Y, Z”. Template zawiera wszystkie architectural requirements. Junior nie musi ich znać, tylko wybrać właściwy template.

To trochę jak scaffolding w Rails albo Artisan w Laravelu – nie piszesz boilerplate ręcznie, ale framework narzuca strukturę. Z AI różnica jest taka, że musisz tę strukturę narzucić explicite w prompcie, bo samo AI jej nie zna.

"Najbardziej niebezpieczny kod z AI zwykle nie wygląda podejrzanie. Wygląda dokładnie tak, jakby ktoś wiedział, co robił. Dopiero następny feature weryfikuje, czy rzeczywiście tak było."

Symptomy że masz problem

Jeśli czytasz to i zastanawiasz się czy twój projekt ma ten problem, są konkretne red flags.

Najoczywistszy – klient nie może edytować tekstów przez WordPress admin. Jeśli każda zmiana copy wymaga edycji kodu, masz hardcoding problem. Normalnie w WordPressie 90% tekstów powinno być manageable przez panel admina – przez Gutenberg, ACF, widget areas, menu. Jeśli nie są, coś jest fundamentalnie źle.

Drugi symptom – nie możesz włączyć popularnych pluginów, bo konfliktują z custom code. Próbujesz dodać Yoast SEO i nie widzi twoich produktów? Prawdopodobnie produkty są w custom table zamiast jako CPT. Próbujesz włączyć caching i strona się sypie? Prawdopodobnie inline JS albo hardcoded dynamic content w templates.

Trzeci symptom – każda nowa funkcjonalność wymaga edycji istniejącego kodu zamiast dodania nowego pluginu/modułu. Jeśli żeby dodać wysyłkę maila po złożeniu zamówienia musisz edytować funkcję procesującą zamówienia, nie masz hooków. Jeśli żeby zmienić layout produktu musisz edytować core plugin, nie masz template override system.

Czwarty symptom – deployment breaks things unpredictably. Kod który działał lokalnie przestaje działać na stagingu. Różne hardcoded paths, różne URLs w bazie, brak environment variables. Oznacza że masz environment-specific logic zahardcoded w kodzie.

Piąty symptom – nowy developer potrzebuje tygodni żeby zrozumieć jak coś zmienić. Nie ma spójnej architektury, każdy fragment używa innego pattern, brak dokumentacji co za co odpowiada. Typowe dla kodu generowanego przez AI bez oversight.

Jeśli masz trzy z pięciu, prawdopodobnie siedzisz na bombie zegarowej. Działa teraz, ale każda nowa feature będzie coraz droższa, każda zmiana coraz bardziej ryzykowna. W pewnym momencie łatwiej będzie przepisać niż utrzymywać.

"Jeśli po każdej pozornie prostej zmianie pierwszą myślą jest „lepiej tego nie ruszać”, to architektura prawdopodobnie przestała pomagać już jakiś czas temu. Tylko produkcja była zbyt uprzejma, żeby powiedzieć to od razu."

Lekcje

Prawda jest taka, że AI nie zepsuje twojej architektury. Zepsuje ją brak review i brak zrozumienia, że kod który compiles nie jest tym samym co kod który pasuje do frameworka. WordPress ma bardzo konkretną filozofię – pluggable, extensible, content-first. AI tej filozofii nie zna i nie nauczy się jej z samego faktu, że generuje PHP. Musisz ją wymusić – przez code review, przez conventions w promptach, przez architekturę referencyjną którą trzymasz jak biblii. Albo skończysz z monolitem który technicznie działa, ale którego nikt nie jest w stanie utrzymywać. A to nie jest framework problem. To jest process problem.

Podsumowanie

To zabawne, jak często największym problemem nie okazuje się AI, tylko pewność, że skoro kod przeszedł testy i wygląda sensownie, to na pewno jest zgodny z architekturą. Produkcja ma wyjątkowy talent do udowadniania, że framework pamięta o swoich zasadach znacznie dłużej niż autor promptu.

Po kilku miesiącach utrzymywania takich projektów przestajesz oceniać wygenerowany kod po tym, ile linijek napisał za ciebie model. Znacznie ważniejsze staje się to, ile linijek będziesz musiał usunąć za pół roku, kiedy pierwszy większy feature zderzy się z zahardcodowaną logiką i brakiem punktów rozszerzalności.

WordPress wybacza naprawdę wiele, ale bardzo długo pamięta każdą próbę ominięcia jego architektury. Hooki, CPT czy standardowe API często wyglądają jak niepotrzebny narzut. Do momentu, aż klient poprosi o coś, co według wszystkich estymacji miało zająć godzinę, a kończy się tygodniowym refaktorem.

AI wygeneruje kod w kilka sekund. Konsekwencje złych decyzji architektonicznych potrafią kompilować się przez kolejne dwa lata. I tego niestety nie naprawi żaden następny prompt.

Zobacz powiązane case studies i analizy

Procesy, architektura i workflow powiązane z tematami poruszanymi w tym materiale – od integracji i realtime systems po automatyzacje operacyjne.

Inne

Pozostałe artykuły

Webhook Reliability Patterns: czego nauczyłem się debugując phantom calls w systemie VoIP
Distributed state w systemach realtime działa poprawnie tylko do momentu, w którym różne warstwy infrastruktury zaczynają posiadać sprzeczne informacje o tym samym połączeniu.
Confidence score OCR często wprowadza w błąd. Sprawdź, jak skutecznie walidować dane i ograniczyć błędy w produkcji.