Przejdź do treści

Naprawić czy przepisać stronę: 8 sygnałów które rozstrzygają

11 min czytaniaZaawansowany

Osiem konkretnych sygnałów z naszych audytów: TypeScript, framework, wtyczki, Core Web Vitals, baza, auth, testy. Jak w 30 minut zdecydować, czy naprawiać, czy pisać od nowa.

Dwie translucentne struktury obok siebie. Lewa: warstwowa, pofragmentowana, z widocznymi szwami (legacy do naprawy). Prawa: monolityczna, czysta, krystaliczna (rewrite). Wąska   szczelina próżni między nimi to "moment decyzji".

Wstęp

Co kilka tygodni rozmawiamy z kimś, kto kilka miesięcy temu zlecił stronę freelancerowi i teraz nie wie, co dalej. Strona działa, ale ledwo. Google nie indeksuje, ładowanie idzie sześć sekund, formularz nie wysyła maili. Pytanie pada zawsze podobnie: naprawić, czy napisać od nowa.

Uczciwa odpowiedź jest "to zależy", ale to słabo pomaga. Lepsza odpowiedź to osiem sygnałów, które oglądamy w każdym audycie. Jeśli wiesz, na co patrzeć, większość konsultacji można rozstrzygnąć w pół godziny.

Niżej rozpisuję każdy sygnał: jak go sprawdzić, jaki próg jest czerwony, co to znaczy w pieniądzach. Na końcu prosty framework: liczysz red flagi i dostajesz decyzję.

Krótka uwaga na początek. Piszę to z perspektywy stacku, którego sami używamy najczęściej: Next.js, TypeScript, Postgres. Dla Symfony, Rails czy Laravela logika jest ta sama, liczby trochę inne. Jeśli prowadzisz coś bardzo specyficznego (legacy COBOL, Magento, Salesforce Commerce), to nie jest artykuł dla Ciebie i nie pomoże Ci go przeczytać do końca.

1. TypeScript coverage i gęstość any

Próg:

StanSygnał
< 30% plików ma typyCzerwony
30-70%, lub `strict: false`Żółty (typowo "dodano TS w połowie projektu i porzucono")
> 70% z `strict: true`, gęstość `any` < 1 na 100 liniiZielony

Jak sprawdzić w 5 minut:

bash
1# Stosunek TS do JS
2find src -name "*.ts" -o -name "*.tsx" | wc -l
3find src -name "*.js" -o -name "*.jsx" | wc -l
4
5# Gęstość any
6rg ': any\b|as any\b' --type ts | wc -l
7
8# Czy strict mode włączony
9cat tsconfig.json | grep -A2 '"strict"'
10
11# Ile @ts-ignore / @ts-expect-error
12rg '@ts-(ignore|expect-error)' --type ts | wc -l

Co to znaczy w pieniądzach: zmiana nazwy pola w API przy braku typów to dzień szukania w 12 plikach, każdy może kompilować się i wybuchać runtime. Z typami: kompilator pokazuje 12 miejsc w 30 sekund. Godzina seniora to 100-200 zł zależnie od stawki. W projekcie sześciomiesięcznym brak typów to dodatkowe 80-150h roboty na utrzymaniu, którą i tak ktoś zapłaci.

Konkret z naszej diagnostyki: zdarza nam się projekt Next.js 14 z tsconfig w którym strict: false, kilkaset wystąpień : any, kilkadziesiąt @ts-ignore. Na pierwszy rzut oka "ma TypeScript". W praktyce: typy nikogo nie chronią, każda zmiana to ruletka.

Dlaczego to jest dziś ważniejsze niż kilka lat temu: asystenty AI (Cursor, Copilot) chętnie dopisują function processData(data: any): any, jeśli strict jest wyłączony. Lint tego nie złapie. Rok takiej współpracy z asystentem i masz codebase, który "ma TypeScript", ale mógłby równie dobrze go nie mieć.

2. Wiek frameworka kontra aktualna stabilna wersja

Próg:

StanSygnał
0-1 wersji głównej do tyłuZielony
2 wersje główne do tyłu (zwykle 6-12 mies. zaległości)Żółty
3+ wersji głównych do tyłuCzerwony, każda zaległa wersja to akumulowane CVE

Jak sprawdzić:

bash
1npx npm-check-updates
2# albo bezpośrednio
3cat package.json | jq '.dependencies'
4# i porównać z npm view <pkg> version

Konkretne progi po frameworku (stan na 2026):

  • Next.js: < 13 (brak App Router stabilnego, brak React 18 features)
  • React: < 18 (brak Suspense, brak concurrent features)
  • WordPress: < 6.0 (brak FSE, niezgodne z PHP 8.x)
  • Vue: 2 (Vue 3 ma 5+ lat, Vue 2 EOL od końca 2023)
  • Symfony LTS: < 6.4

Co to znaczy w pieniądzach: każda major version migration to typowo 2-3 tygodnie pracy seniora plus testowanie. Trzy zaległe majory to półtora-dwa miesiące samego upgrade'u, zanim cokolwiek nowego dodasz. Plus security: brak supportu znaczy, że każdy CVE pozostaje otwarty, dopóki nie zaktualizujesz.

Konkret: Fundacja Znajdki ze schronisk dla zwierząt przyszła do nas z Next.js 12, React 17, ostatni commit w package.json z 2023. Trzy majory Next, jeden major React. Sam upgrade to dwa tygodnie. Refaktor pod App Router to kolejne dwa. Razem miesiąc, zanim dodaliśmy nową funkcję.

3. Liczba wtyczek (WordPress) i zewnętrznych zależności (npm)

Próg:

StanSygnał
WordPress > 25 wtyczek aktywnychCzerwony
npm > 150 deps top-levelCzerwony, > 300 = krytyczny
Initial JS bundle > 500 KBCzerwony

Jak sprawdzić:

bash
1# WordPress
2wp plugin list --status=active --format=count
3
4# npm
5cat package.json | jq '.dependencies | keys | length'
6
7# Ile faktycznie używanych
8npx depcheck
9
10# Bundle size
11npx next build # zobacz "First Load JS" w outpucie

Patterny do wykrycia w 5 minut:

  • 6+ wtyczek SEO (Yoast + RankMath + AIOSEO razem, każda chce być pierwsza)
  • 3+ wtyczek cache (W3 Total Cache + WP Rocket + LiteSpeed)
  • Elementor + 4-5 add-onów (Elementor Pro + Essential Addons + Ultimate Addons)
  • W repo npm: lodash + ramda + underscore jednocześnie (3 biblioteki utility, każda 80 KB, każda używana w 2-3 miejscach)

Co to znaczy w pieniądzach: każda wtyczka to nowa powierzchnia dla CVE (średnio 1-2 critical CVE miesięcznie w top 50 plugin), każda to performance penalty (10-50 KB CSS+JS minimum), każda to potencjalny conflict z innymi przy upgrade'ach. Strona z 35 wtyczkami WP nie da się utrzymać przez 1 osobę poniżej połowy etatu.

Konkret: klient z e-commerce miał 47 aktywnych wtyczek WordPress. WooCommerce plus 12 jego rozszerzeń, 4 SEO, 3 cache, 8 marketingowych. PageSpeed mobile 32, LCP 8.7s. Pierwsza diagnoza: deactivate wszystkich i sprawdzenie samej strony bez nich. Wynik: LCP 2.1s. Czyli 6.6 sekundy ładowania to były same wtyczki. To jest moment, w którym refactor jest tańszy niż utrzymanie.

4. Lighthouse i Core Web Vitals

Próg (mobile, dane realne):

MetrykaCzerwony
LCP> 4s
INP> 500ms
CLS> 0.25
Lighthouse score (mobile)< 50, < 30 = krytyczny

Jak sprawdzić:

bash
1# Local lab data
2npx lighthouse https://example.com --emulated-form-factor=mobile
3
4# Field data (real users)
5# pagespeed.web.dev → CrUX (Chrome User Experience Report)
6# Search Console → Core Web Vitals

Klucz, który wielu pomija: patrz na dane terenowe (od realnych użytkowników), nie tylko dane laboratoryjne (Lighthouse lokalnie). Laboratorium pokazuje, co teoretycznie możliwe, teren – co dzieje się u użytkowników. Jeśli lokalnie 90, a w terenie 35, to znaczy, że Twój CDN, hosting albo geografia użytkowników psują doświadczenie, którego lokalnie nie zobaczysz.

Co to znaczy w pieniądzach: od 2024 Core Web Vitals są w kryteriach rankingowych Google (Page Experience). Strona z LCP 6s nie ma szans z konkurencją, która ma LCP 1.8s na tym samym słowie kluczowym. Plus konwersja: wedle badania Google z 2017 (SOASTA) 53% odwiedzin mobile zostaje porzuconych, jeśli ładowanie trwa dłużej niż 3 sekundy. Każda kolejna sekunda boli.

Konkret: klient z usługami medycznymi miał Lighthouse mobile 28, LCP 7.3s. Po trzech tygodniach refaktoru (usunięcie 14 wtyczek, optymalizacja obrazów, krytyczny CSS inline): Lighthouse 87, LCP 1.9s. Ruch z Google urósł 2x w 60 dni, na tych samych słowach kluczowych, bez zmian w treści. Same Core Web Vitals.

5. Zakładka Coverage – ile JavaScriptu jest martwe

Próg:

StanSygnał
> 50% niewykorzystanego JS przy pierwszym renderzeŻółty
> 70%Czerwony
> 85% (typowy WordPress + Elementor)Krytyczny

Jak sprawdzić w 2 minuty:

  1. Otwórz Chrome DevTools (Cmd+Opt+I)
  2. Cmd+Shift+P, wpisz "Show Coverage"
  3. Kliknij record (czerwone kółko)
  4. Reload strony
  5. Stop record
  6. Sortuj po "Unused Bytes"

Co tam zobaczysz w typowym WordPress + Elementor:

  • jQuery (90 KB) ładuje się dla strony bez ani jednego użycia jQuery
  • 4 wersje React (Elementor add-onów, każdy z własną wersją)
  • Cały Bootstrap CSS, gdy używana jest jedna klasa
  • lodash 70 KB, gdy używasz tylko _.debounce

Co to znaczy w pieniądzach: każde 100 KB niewykorzystanego JS to około 200ms parsowania na średnim Androidzie plus transfer na rachunku użytkownika. Na ruchu zdominowanym przez mobile to znacząca konwersja, a w przypadku stron, które robią wiele żądań (SPA, dashboardy), efekt akumuluje się przy każdej nawigacji.

Konkret: klient na Next.js 13 z initial bundle 840 KB. Coverage pokazał 78% niewykorzystanego kodu. Powód: import * from 'lodash' w trzech miejscach, każde ściągało całą bibliotekę. Plus moment.js z całymi locales, z którego używana była jedna funkcja. Po zmianie na imports per-function i wymianie moment na date-fns: bundle 280 KB, LCP minus 2.1s. Jeden dzień pracy.

6. Architektura bazy danych

Próg:

StanSygnał
Schema w kontroli wersji (`prisma/`, `migrations/`)Zielony
Schema istnieje, ale ALTER TABLE robione ręcznie w prodŻółty
"Trzeba zapytać Tomka, on wie"Czerwony

Jak sprawdzić:

  • Czy istnieje katalog prisma/, migrations/, db/, supabase/migrations/?
  • Czy w README jest sekcja "Database setup"?
  • Uruchom: \d+ <table> w psql, czy są foreign keys, indeksy, constrainty?

Antypatterny do wyłapania:

  • Tabele bez created_at / updated_at
  • Brak foreign keys (relacje są w kodzie, ale nie wymuszone na poziomie DB)
  • Kolumny data1, data2, extra_field, comment_2, tag1, tag2...
  • JSON jako string w VARCHAR(255) zamiast JSONB
  • Tabele po polsku obok angielskich, mieszane konwencje
  • Hasło użytkownika w trzech tabelach jednego projektu nazwane: pass, password_hash, hashed_password

Co to znaczy w pieniądzach: bez migracji w kontroli wersji każdy refaktor zaczyna się od archeologii. Co jest w prod, co w stagingu, co w devie, dlaczego się różnią. Zwykle tydzień samej diagnozy, zanim dotkniesz kodu aplikacyjnego. Plus nie da się odtworzyć środowiska, więc nowy developer dostaje sql dump z prod, co przy danych osobowych jest problemem RODO.

Konkret: projekt klienta z 2019, MySQL, brak migracji w git. 47 tabel, 23 z nich miały hasło w innej formie. Trzy tabele miały kolumny tag1, tag2, ..., tag10. Diagnoza zajęła nam 6 dni samego mappingu, zanim dotknęliśmy logiki aplikacyjnej.

7. Auth napisany od zera

Próg:

StanSygnał
Standardowa biblioteka (Auth.js, Supabase Auth, Clerk, Firebase)Zielony
Custom z `bcrypt` + JWT, ze wszystkimi flagami bezpieczeństwaŻółty (warto audyt, zwykle OK)
Custom z `md5`/`sha1` haseł, JWT bez czasu wygaśnięcia, brak ograniczenia liczby żądańCzerwony

Jak sprawdzić:

bash
1# Czy jest standardowa biblioteka
2cat package.json | grep -E "next-auth|@auth|@supabase/auth|@clerk|firebase-auth"
3
4# Czy custom
5rg "bcrypt|argon2|scrypt" --type ts --type js
6
7# Hashing dziadowski
8rg "createHash\('md5'|createHash\('sha1'" --type ts --type js

Konkretne błędy, które znajdujemy regularnie:

  • Brak rate limit na /login (atak słownikowy w 30 sekund)
  • Reset hasła linkiem bez czasu wygaśnięcia (token żyje wiecznie)
  • Reset hasła przez HTTP zamiast HTTPS (gdy ktoś zrobił reverse proxy bez przemyślenia)
  • JWT w localStorage (XSS w jednym formularzu = przejęcie konta na zawsze)
  • Cookies bez Secure, HttpOnly, SameSite=Lax
  • Potwierdzenie e-maila plain text z linkiem klikalnym (magnes na phishing)

Co to znaczy w pieniądzach (i nie tylko): custom auth to ryzyko CVE, ryzyko skargi do UODO (RODO art. 32, środki techniczne), ryzyko incydentu, który dla SaaS B2B oznacza realną szansę na utratę firmy reputacyjnie. Standardowa biblioteka znaczy, że ktoś inny utrzymuje, ktoś inny audytuje, łatwiej spełnić obowiązki RODO i NIS2.

Konkret: widzieliśmy SaaS B2B z customowym JWT bez czasu wygaśnięcia, trzymanym w localStorage. XSS w jednym z formularzy oznaczałby, że atakujący ma dostęp do konta klienta na zawsze, bez możliwości unieważnienia. Naprawa „z doskoku” niemożliwa bez przepisania wszystkich endpointów. To jest prawdziwy moment, w którym tylko rewrite ma sens, niezależnie od pozostałych sygnałów.

Na co uważać przy współpracy z asystentem AI: Cursor przy "dodaj logowanie" zaproponuje bcrypt + JWT, jeśli sam go nie poprowadzisz. Nie wie, że masz już Supabase z Auth gotowym do użycia. Jeśli developer kopiuje sugestię bez weryfikacji, dostajesz custom auth wtedy, kiedy go nie chciałeś.

8. Testy i CI/CD

Próg:

StanSygnał
0 testówCzerwony, każda zmiana to ruletka
< 30% pokrycia testami przy nietrywialnym kodzieŻółty
> 60% z testami integracyjnymi i E2E na ścieżkach krytycznychZielony

Jak sprawdzić:

bash
1# Czy są testy
2find . -name "*.test.*" -o -name "*.spec.*" | wc -l
3
4# Czy jest CI
5ls -la .github/workflows/ .gitlab-ci.yml 2>/dev/null
6
7# Coverage report (jeśli skonfigurowany)
8npm test -- --coverage

Co weryfikować poza istnieniem:

  • Czy testy faktycznie się uruchamiają (często jest folder __tests__ z pięcioma plikami, które rzucają błąd przy npm test)
  • Czy CI faktycznie blokuje merge przy fail (required check w settings)
  • Czy są testy E2E (Playwright/Cypress) na ścieżkach krytycznych (logowanie, zakup, formularz kontaktowy, płatność, reset hasła)

Co to znaczy w pieniądzach: bez testów refaktor jest niemożliwy bez regresji. Każda zmiana to ręczne klikanie po stronie. W projekcie sześciomiesięcznym brak CI/CD to dodatkowe 40-60h ręcznego QA, które nikt nie wycenił, a ktoś musi zrobić.

Konkret: klient z e-commerce, zero testów, zero CI. Pierwsza naprawa po migracji złamała coś w panelu admina, zauważone trzy dni później przez klientów telefonicznie. Drugi miesiąc po migracji dodaliśmy Playwright na 8 ścieżkach krytycznych (logowanie, dodaj do koszyka, checkout, płatność, faktura, anulowanie, reset hasła, wyszukiwanie). Czas ręcznego QA spadł z 4h na wydanie do zera.

AI-slop w testach: Cursor pisze testy, które nie działają, bo mockują wszystko, w tym funkcję, którą rzekomo testują. Łatwo poznać: test ma expect(true).toBe(true) jako asercję albo cała logika domeny jest zamockowana. Test się "uda", regresji nie wykryje.

Framework decyzyjny

Policz red flags z 8 sygnałów. Każdy jeden punkt.

Liczba red flagsDecyzjaCzas naprawyCzas rewrite
0-2Naprawa, oszczędzasz 60-80% kosztu2-6 tyg.4-6 mies.
3-5Kalkulacja, decyzja zależy od deadline i budżetu8-16 tyg.4-6 mies.
6-8Tylko rewrite, ale w kawałkach < 3 mies. (Strangler Pattern), nie big bangn/a4-8 mies. w kawałkach

Big bang rewrite (cały system od zera, jednym podejściem) ma sens tylko wtedy, gdy strona ma mniej niż 5 podstron, niski ruch i brak integracji zewnętrznych. We wszystkich innych przypadkach Strangler Pattern wygrywa: stawiasz nowy stack obok starego, migrujesz po jednym module, stary system gaśnie naturalnie. Wymaga to dobrego routingu i koordynacji, ale daje Ci możliwość zatrzymania w dowolnym momencie, jeśli coś się posypie.

Co dalej

Trzy ścieżki, zależnie od tego, gdzie jesteś.

Jeśli czytasz to z ciekawości i chcesz głębiej: tu jest pełny raport (22 min, dwa studia przypadku krok po kroku, kontekst rynkowy, kiedy WordPress nadal ma sens).

Jeśli masz konkretną stronę i chcesz subiektywnej oceny: wyślij URL na audyt@epko.tech z dopiskiem "8 sygnałów". W 48h odeślę Ci, jak wypada w każdym z ośmiu punktów. Bez zobowiązań, bez handlowca, jedna strona A4.

Jeśli wynik jest zły i chcesz pełny audyt techniczny: Lighthouse + analiza coverage + przegląd bazy + audyt auth + plan remediacji w 5 dni roboczych. Cena zależy od skali, da się ją oszacować po 15-minutowej rozmowie. Umów się.

Jedno na koniec. Te 8 sygnałów daje Ci 70% odpowiedzi w pół godziny. Pozostałe 30% to rozmowa, w której pytamy o rzeczy, których nie da się sprawdzić skanerem: Twój roadmap produktowy, planowane integracje, kompetencje zespołu, deadline. Najtańszy refaktor to ten, którego nie robimy, jeśli i tak odejdziesz od tej strony za pół roku.

Najczęściej zadawane pytania

Jak długo trwa Wasz audyt 8 sygnałów?
Subiektywna ocena 8 sygnałów, jedna strona A4, wraca do Ciebie w 48 godzin od podania URL. Pełny audyt techniczny (Lighthouse + coverage + przegląd bazy + przegląd auth + plan remediacji) trwa 5 dni roboczych i kończy się raportem z priorytetami.
Czy WordPress to zawsze zły wybór?
Nie. Dla bloga z dwoma wpisami miesięcznie albo prostej strony statutowej WordPress jest sensowny i tańszy w utrzymaniu niż custom Next.js. Problem zaczyna się tam, gdzie wchodzi skala, dynamika, wymagana szybkość ładowania albo widoczność oparta o AI. Wtedy każda kolejna wtyczka jest długiem technicznym, który ktoś kiedyś zapłaci.
Co znaczy „rewrite", jeśli moje pytanie brzmi: „może wystarczy zmienić framework"?
Rewrite oznacza, że substancja aplikacji (logika biznesowa, schemat bazy, treść) zostaje, ale warstwa kodu jest pisana od zera, zwykle na innym frameworku albo na nowszej wersji tego samego. Big bang rewrite to wymiana wszystkiego jednym podejściem. Strangler Pattern to wymiana modułami: stary system działa równolegle, ruch przepinasz po jednym module.
Mam 3-5 red flag. Co realnie zrobić?
To jest strefa kalkulacji. Decyzja zależy od dwóch zmiennych: budżet i deadline. Jeśli deadline jest poniżej 3 miesięcy i budżet napięty, naprawa wciąż ma sens. Jeśli deadline 6+ miesięcy i planujesz inwestować w produkt długoterminowo, rewrite (Strangler) zaczyna się opłacać. Pomocne pytanie: czy za pół roku ten sam zespół i ten sam stack będzie nadal właściwy?
Asystenty AI (Cursor, Copilot) pomagają czy szkodzą przy refaktorze?
Pomagają, jeśli prowadzisz je świadomie: weryfikujesz każdą sugestię, wymagasz `strict: true` w TypeScript, używasz standardowych bibliotek (Auth.js, Supabase Auth), nie kopiujesz kodu bez czytania. Szkodzą, jeśli akceptujesz pierwszą sugestię, mockujesz testy żeby przeszły, albo dopisujesz `: any` dla wygody. Cursor nie wie co masz w stacku, więc każda sugestia wymaga oceny.

Czy Twój produkt cyfrowy spełnia wymogi UE?

EAA, WCAG, RODO, NIS2. Te regulacje już obowiązują. Podaj adres, sprawdzimy zgodność z prawem UE. Za darmo, wyniki w 48h.

WCAG 2.1 AARODO / cookiesNIS2 / bezpieczeństwoWyniki w ciągu 48h
Pomaga nam dobrać zakres audytu i pierwsze rekomendacje.

Co dokładnie dostaniesz w 48 godzin

Krótki, konkretny dokument z najważniejszymi rekomendacjami. Bez zobowiązań i bez pokazówki sprzedażowej.

Co dostaniesz

  • Krótki raport PDF (2-3 strony) z oceną technicznych aspektów WCAG, RODO i NIS2
  • Top 3 ryzyka techniczne, które warto zaadresować w pierwszej kolejności
  • Listę quick winów, które możesz wdrożyć sam lub z dowolnym wykonawcą
  • Opcjonalną krótką rozmowę online, jeśli chcesz dopytać o wyniki

Kto to robi

  • Skan prowadzi członek zespołu EPKO, wspierany automatycznymi narzędziami (Lighthouse, axe-core, własne checklisty)
  • Masz bezpośredni kontakt z osobą, która podpisała się pod raportem, bez account managerów
  • Raport pokrywa warstwę techniczną, nie zastępuje formalnego audytu prawnego ani certyfikacyjnego
  • Jeśli zdecydujesz się na współpracę nad naprawą, do projektu wchodzą Eryk (CTO) lub Patryk (CEO)

W jakiej formie

  • PDF wysyłany mailem, dostępny dla czytników ekranu
  • Krótkie podsumowanie wyników w treści odpowiedzi
  • Materiały zostają u Ciebie, możesz je przekazać działowi prawnemu lub IT
  • Czas: 48 godzin od potwierdzenia zgłoszenia, w dni robocze

Najczęstsze pytania o audyt