Pełna transparentność na start
Ten portal — emdashcms.pl — postawiłem jako pokazówkę EmDash CMS. Razem z bliźniaczą stroną na WordPressie (emdash.pl) były głównymi obiektami badania, które właśnie opublikowałem na blogu SHIFT64. Wszystkie cyfry, które za chwilę zobaczysz, to liczby tej strony — tej, którą właśnie czytasz.
I nie wyglądają dobrze.
emdashcms.pl jest naszym informacyjnym portalem o EmDash CMS — ma być uczciwym źródłem o tej technologii, więc musi pokazywać też te wyniki, które są dla niej niewygodne. Stąd ten artykuł tutaj, a nie tylko na shift64.com.
Co właściwie porównałem
Zbudowałem dwie strony z identyczną treścią:
- emdashcms.pl — Astro 6 + EmDash + Cloudflare Workers + D1 (oficjalny szablon
emdash-blog, bez modyfikacji) - emdash.pl — ręcznie napisany motyw WordPressa na PHP 8.4 + MySQL, hosting Hetzner we Frankfurcie, tylko Object Cache (żadnego full-page cache, żadnego Elementora, żadnych wtyczek-potworów)
Obie strony były scaffoldowane z Claude Code. Świadomie nie włączyłem cache HTML po stronie WordPressa — Worker EmDasha i tak na każde żądanie wykonuje pracę (pyta D1, składa HTML), więc porównanie z gotowym pliczkiem byłoby nieuczciwe.
Z serwera VPS w Warszawie odpaliłem curl 4 732 razy w 182 przebiegach przez ~3,5 doby, na 13 stronach, w losowej kolejności, bez keep-alive, z wymuszaniem zimnych startów Workera w rytmie 3-godzinnym.
Wyniki — czysty czas przetwarzania serwera
TTFB minus DNS, TCP i SSL — czyli sama praca backendu (zapytania do bazy, składanie HTML):
- Średnia: EmDash 543 ms vs WordPress 84 ms — WP 6,4× szybszy
- Mediana: 543 ms vs 69 ms — WP 7,9× szybszy
- P95: 864 ms vs 148 ms — WP 5,8× szybszy
- P99: 1 121 ms vs 234 ms — WP 4,8× szybszy
- Maksimum: 2 196 ms vs 246 ms — WP 8,9× szybszy
Najgorszy pomiar WordPressa (246 ms) jest szybszy od najlepszego pomiaru strony postu na EmDashu. To nie jest margines, który się dooptymalizuje — to inna kategoria opóźnienia.
Skąd ta różnica?
Z architektury, nie z kodu. Strona postu na EmDashu wymaga sekwencji 8+ zapytań do D1 (entry → tagi → powiązane wpisy → ich tagi → widgety w sidebarze i stopce). Każdy round-trip to 40–100 ms na edge'u. WordPress robi to samo na lokalnym MySQL-u na tej samej maszynie w 0,5 ms łącznie (Object Cache + OPcache), przy 2–3 zapytaniach na całą stronę.
Plus: zimne starty Workerów są realne i mają dzienny cykl. W godzinach pracy (10:00 UTC) 35% żądań przekraczało 800 ms, widziałem maksima 2,2 s. W nocy spadało do ~8%. WordPress: zero efektu zimnego startu — pula PHP-FPM nigdy nie idzie spać.
Co próbowałem naprawić
Po pierwszej rundzie przepisałem stronę EmDasha:
- `server:defer` na widget areas — sidebar i stopka wychodzą z odpowiedzi początkowej, ładują się dogrywką. Wymaga ręcznego pisania komponentów-wrapperów (dyrektywa nie działa na importach z
node_modules) i fallbacków-szkieletów. Tego nie ma w dokumentacji jako wymaganego kroku. - Batchowanie tagów (`getTermsForEntries`) — niedokumentowane API EmDasha. Zabija N+1 na listingach: zamiast jednego query na każdy z N postów, jeden
WHERE entry_id IN (...). Znalazłem przez czytanie sourców. - Edge cache na poziomie Workera —
caches.open()ręcznie wsrc/worker.ts, bo Workery na custom domenie domyślnie omijają CDN cache Cloudflare.Cache-Controlpo prostu nie zadziała, dopóki nie owiniesz handlera.
Co dało: średnia 543 → 322 ms (−41%), P99 1094 → 424 ms (−61%), maksimum 2196 → 745 ms (−66%). Realna wygrana. Niewystarczająca — WordPress dalej był 4,1× szybszy. Te ~320 ms to floor, którego nie ruszę bez D1 read replicas.
Werdykt zewnętrznego eksperta
Wykresy z Search Console (czas pobierania) wysłałem ślepo Jackowi Żmudzińskiemu (Head of GEO & SEO w Makolab). Bez etykiet, bez komentarza. Cztery tiery:
- ~1 300 ms — „do złomu i przepisać” → tutaj wylądowała strona postu EmDasha na zimnych startach
- typowy zoptymalizowany WP — „normalne, zgodne z oczekiwaniami” → tutaj wylądował
emdash.pl - dobrze nastrojony WP — „przyzwoite”
- klasa Astro — „wzorowe” → klasa, w której EmDash powinien być
Ta sama technologia (Astro), do której EmDash dokłada własną warstwę danych, dostaje najwyższą notę. Aktualne wykonanie EmDasha — najniższą.
Po tygodniu w Search Console: WordPress ma 4,3× więcej kliknięć, 3,9× wyższy CTR i wygrywa rankingiem nawet na zapytaniu „emdash cms”. Strona pisana o EmDashu przegrywa na własnej brandowej frazie z hand-coded WP, do którego nikt nie zaglądał od dnia publikacji.
Czy to znaczy, że EmDash jest „zły”?
Nie. To znaczy, że jest zły dla mojego use case'a — małych stron firmowych z niewielkim ruchem, gdzie Googlebot to znaczna część odwiedzin, a Worker przez większość doby śpi. Wszystkie problemy z benchmarku odwracają się przy dużym ruchu: Worker nie stygnie, edge cache faktycznie się zapełnia, D1 ma ciepłe shardy. Nie zmierzyłem tego scenariusza i nie zdziwiłbym się, gdyby tam EmDash wyglądał świetnie.
Wracam do EmDasha kiedy Cloudflare wyda D1 read replicas (jedyna zmiana, która zbije floor latency 5–10×) i gdy experimental.cache Astro doczeka się providera dla adaptera Cloudflare. Wtedy będę mógł go z czystym sumieniem polecić małym firmom — co pierwotnie było całym sensem tego eksperymentu.
Pełny artykuł, dane, repo
To wersja skrócona. Pełny artykuł — z rozkładem na godziny doby, wpływem płatnego planu Workers, screenshotami Query Monitor z WordPressa, komentarzem Jacka Żmudzińskiego i polemiką z Maćkiem Palmowskim — jest na blogu SHIFT64:
I Bought the Domain Before I Ran the Test. EmDash Still Lost to WordPress.
Oba repozytoria są publiczne (bench.sh, analyze.py, results.csv, surowe dane do reanalizy) — linki w pełnym artykule. Jeśli ktoś znajdzie konfigurację, która zbija EmDasha do klasy WordPressa bez ręcznego przepisywania połowy warstwy danych — z chęcią opublikuję follow-up.
Brak komentarzy