Przejdź do treści

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

Patryk KorzeniowskiPatryk Korzeniowski11 min czytania

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 major version behindZielony
2 majors behind (zwykle 6-12 mies. zaległości)Żółty
3+ majors behindCzerwony, każdy zaległy major 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, real-world):

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 field data (real users), nie tylko lab data (Lighthouse local). Lab pokazuje co teoretycznie możliwe, field co dzieje się u użytkowników. Jeśli lab 90, a field 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 keywordzie. 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 keywordach, bez zmian w treści. Same Core Web Vitals.

5. Coverage tab — ile JavaScriptu jest martwe

Próg:

StanSygnał
> 50% unused JS przy initial paintŻół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 unused JS to około 200ms parse time na średnim Androidzie plus bandwidth na rachunku użytkownika. W mobile-heavy traffic 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 bundle initial 840 KB. Coverage pokazał 78% unused. 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 version control (`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 version control 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 securityŻółty (warto audyt, zwykle OK)
Custom z `md5`/`sha1` haseł, JWT bez expiry, brak rate limitCzerwony

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 bugi, które znajdujemy regularnie:

  • Brak rate limit na /login (atak słownikowy w 30 sekund)
  • Reset hasła linkiem bez expiry (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
  • Plain-text email confirmation z linkiem klikalnym (phishing magnet)

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 custom JWT bez expiry, stored 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% coverage przy nontrivialnym kodzieŻółty
> 60% z testami integracyjnymi i E2E na critical pathsZielony

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 critical paths (logowanie, zakup, formularz kontaktowy, payment, password reset)

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 critical paths (login, dodaj do koszyka, checkout, payment, faktura, anulowanie, password reset, wyszukiwanie). Czas ręcznego QA spadł z 4h na release do zera.

AI-slop signal w testach: Cursor pisze testy, które nie biegają, bo mock'ują 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 case studies 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 + coverage analysis + DB review + auth audit + 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 + DB review + auth review + 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 high-level oceną zgodności WCAG, RODO i NIS2
  • Top 3 ryzyka, 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

  • Audyt prowadzi specjalista compliance z zespołu EPKO, wspierany automatycznymi skanerami (Lighthouse, axe-core, własne checklisty)
  • Masz bezpośredni kontakt z osobą, która podpisała się pod raportem, bez account managerów
  • 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

Czy muszę coś podpisać przed audytem?
Nie. Audyt jest darmowy i niezobowiązujący. Nie wysyłamy umów ani NDA na tym etapie. Jeśli zdecydujesz się na współpracę, dopiero wtedy podpisujemy umowę powierzenia danych i kontrakt na zakres prac.
Jak wykorzystujecie dane z mojego audytu?
Wyłącznie do przygotowania raportu dla Ciebie. Nie udostępniamy ich nikomu, nie wykorzystujemy w marketingu i nie tworzymy z nich case study bez Twojej pisemnej zgody. Dane usuwamy 90 dni po wysłaniu raportu, chyba że zaczniemy współpracę.
Czy audyt to tak naprawdę próba sprzedaży?
Nie. Raport zawiera konkretne ryzyka i quick winy, które możesz wdrożyć sami lub z dowolnym wykonawcą. Jeśli quick winy wystarczą, mówimy to wprost, nawet kosztem zlecenia.
Czy potrzebujecie dostępu do mojego systemu?
Nie do raportu wstępnego. Pracujemy na publicznie dostępnych częściach strony oraz na Twoich odpowiedziach w formularzu. Dostęp do panelu admin lub kodu omawiamy dopiero, jeśli wejdziemy w fazę naprawy.
Co jeśli moja strona jest dopiero w budowie?
Wtedy audyt jest jeszcze ważniejszy. Zaznacz „Jeszcze nie uruchomiliśmy" w skali, a my przeanalizujemy specyfikację, mockupy lub repozytorium i powiemy, co zaplanować przed wdrożeniem, żebyś nie poprawiał tego później.