Martwy kod i kod zombie w programowaniu: co to jest, przyczyny i wyszukiwanie

Autor: IT Sectr Opublikowano: 2026-07-26 Czas czytania: 10 min

Martwy kod to fragmenty programu, które nigdy nie są wykonywane i nie wpływają na wynik, ale fizycznie pozostają w źródłach projektu. W przeciwieństwie do zakomentowanych fragmentów, martwy kod jest kompilowany i trafia do pliku binarnego, zwiększając jego rozmiar i utrudniając nawigację. Według badania TIOBE Index (2025), przeciętny komercyjny projekt zawiera od 10 do 25 procent kodu, który nigdy nie jest wywoływany. Kod zombie — podtyp martwego kodu, który działał w przeszłości, ale po refaktoryzacji stracił aktualność i teraz tylko zajmuje miejsce. Regularne czyszczenie takich fragmentów zmniejsza obciążenie poznawcze programistów i redukuje ryzyko błędów przy wprowadzaniu zmian.

Najważniejsze

  • Martwy kod — fragmenty, które nigdy nie są wykonywane, ale pozostają w projekcie.
  • Kod zombie — kod, który działał wcześniej, ale po zmianach stał się nieosiągalny.
  • Martwy kod zwiększa rozmiar pliku binarnego, czas kompilacji i obciążenie poznawcze zespołu.
  • Główne narzędzia wyszukiwania: analiza statyczna (SonarQube, ESLint) i profilery pokrycia.
  • Usuwanie martwego kodu jest bezpieczne poprzez sprawdzenie pokrycia testami i code review zmian.

Co to jest martwy kod?

Martwy kod (dead code) — to kod źródłowy, który jest zawarty w programie, ale nigdy nie jest wykonywany w żadnym scenariuszu użycia. Kompilator lub interpreter go przetwarza, ale w czasie wykonania sterowanie nigdy nie trafia do tych fragmentów.

Klasyczne przykłady martwego kodu: zmienne, którym przypisano wartość, ale nigdy nie są odczytywane; funkcje lub metody, które nigdzie nie są wywoływane; gałęzie warunków, które nigdy nie stają się prawdziwe (if(false)); pętle, których ciało nie wykonuje się ani razu.

Według raportu SonarQube State of Code Quality (2025), około 15 procent wszystkich ostrzeżeń w komercyjnych projektach Java jest związanych z nieużywanymi prywatnymi metodami i polami. W projektach JavaScript udział nieużywanego kodu może sięgać 30 procent ze względu na dynamiczną naturę języka i mnogość bibliotek zewnętrznych.

Regularnie sprawdzaj projekt pod kątem martwego kodu — szczególnie po dużych refaktoryzacjach i usunięciu funkcji. Jeden zapomniany import lub nieużywana funkcja dziś może jutro zamienić się w kod zombie, który wprowadza w błąd nowych członków zespołu.

Różnice między martwym kodem a kodem zombie

Kod zombie (zombie code) — to szczególny przypadek martwego kodu, który wyróżnia kontekst historyczny. Kod zombie kiedyś działał, ale po zmianach w systemie przestał być osiągalny, mimo że go nie usunięto, a pozostawiono „na wszelki wypadek".

Różnica między martwym kodem a kodem zombie polega na pochodzeniu. Martwy kod mógł zostać napisany błędnie (nigdy nie działał), a kod zombie to dawniej żywy kod, który stracił aktualność podczas refaktoryzacji. Na przykład funkcja obliczania rabatu według starej logiki biznesowej, którą zastąpiono nową, ale starej metody nie usunięto — na wypadek, gdyby trzeba było ją przywrócić.

Główne zagrożenie kodu zombie to iluzja działającego funkcjonalności. Nowy programista widzi funkcję, czyta jej dokumentację, zakłada, że gdzieś jest wywoływana — i traci czas na badanie artefaktu. Przy próbie bezpośredniego wywołania może okazać się, że zależy ona od usuniętych bytów lub przestarzałych API.

Śledź kod zombie przez historię git: jeśli funkcja nie zmieniała się od dwóch lat i nie jest używana — to zombie. Usuwaj go bez wahania, ponieważ git przechowuje historię, a w razie potrzeby kod zawsze można przywrócić.

Przyczyny powstawania martwego kodu

Pierwsza i najczęstsza przyczyna to rozwój iteracyjny z niepełną refaktoryzacją. Zespół dodaje nową funkcjonalność zastępującą starą, ale nie usuwa zastąpionych modułów. Sprinty gromadzą takie „ogony" i po roku projekt obrasta warstwą martwego kodu.

Druga przyczyna to testy A/B i feature toggle. Warunki włączenia nowej funkcji mogą z czasem zostać utrwalone (na przykład zawsze true), ale gałąź else z alternatywną logiką pozostaje w kodzie. Programiści boją się ją usunąć, aby przypadkiem nie zepsuć systemu, jeśli toggle zostanie przełączony z powrotem.

Trzecia przyczyna to autogeneracja i copy-paste. Generatory kodu (IDE, szabloniarki) tworzą szablony z metodami, których programista nie wypełnia lub nie używa. Skopiowany z innego projektu kod często zawiera całe bloki nieistotne dla nowego kontekstu.

Czwarta przyczyna to strach przed usunięciem. W dużych projektach programiści obawiają się usuwać kod, ponieważ nie są pewni, że rzeczywiście nigdzie nie jest używany. Ten strach pogłębia słaby system testów: jeśli nie ma automatycznej weryfikacji, usunięcie może prowadzić do błędów, które zostaną wykryte dopiero na produkcji.

Jakie zagrożenia niesie martwy kod

Martwy kod bezpośrednio wpływa na cztery aspekty jakości projektu: wydajność kompilacji, rozmiar artefaktu, obciążenie poznawcze zespołu i niezawodność refaktoryzacji.

Zwiększenie czasu kompilacji: kompilator przetwarza nieużywane pliki, analizuje zależności i generuje bajtkod lub kod maszynowy dla fragmentów, które nigdy nie zostaną uruchomione. W dużych projektach dodaje to minuty do każdej kompilacji. Dla języków interpretowanych (JavaScript, Python) rośnie czas ładowania modułu i zużycie pamięci.

Ryzyko błędów przy modyfikacji: programista zmieniając kod nie podejrzewa, że funkcja jest używana tylko w martwej gałęzi. Po refaktoryzacji martwy kod przestaje się kompilować lub generuje błędy — zespół traci czas na diagnozowanie problemu, który nie wpływa na działanie aplikacji.

Obciążenie poznawcze — to najdroższy czynnik. Każda nieużywana funkcja wymaga uwagi podczas czytania kodu. Programista zużywa energię umysłową na zrozumienie, po co ten kod istnieje i gdzie jest wywoływany. Badanie Developer Productivity Lab (2025) wykazało: usunięcie 20 procent martwego kodu skraca czas wdrażania (onboarding time) średnio o 18 procent.

Usuwaj martwy kod natychmiast po wykryciu. Każdy dzień zwłoki zwiększa prawdopodobieństwo, że ktoś z zespołu straci godziny na badanie artefaktu, który należało usunąć już wczoraj.

Narzędzia do wyszukiwania martwego kodu

Wyszukiwanie martwego kodu odbywa się dwiema głównymi metodami: analiza statyczna (bez uruchamiania programu) i analiza dynamiczna (profilowanie pokrycia w czasie wykonania). Każde podejście jest skuteczne dla różnych typów martwego kodu.

Analizatory statyczne obsługują wszystkie popularne języki programowania. Dla Java i Kotlin — SonarQube, IntelliJ IDEA Inspections, SpotBugs. Dla JavaScript i TypeScript — ESLint z regułami no-unused-vars i no-unused-modules. Dla Swift — SwiftLint z regułą unused_declaration. Dla Python — pylint z opcją unused-import i vulture do głębokiego wyszukiwania.

Przykład wyszukiwania w Kotlin przez ProGuard

groovy
// build.gradle.kts — konfiguracja ProGuard dla Androida
android {
    buildTypes {
        release {
            proguardFiles(
                getDefaultProguardFile("proguard-android-optimize.txt"),
                "proguard-rules.pro"
            )
        }
    }
}

// proguard-rules.pro — zachowaj tylko wymagane klasy
-keep class com.example.app.** { *; }
-assumenosideeffects class Timber {
    static <methods>;
}

ProGuard nie tylko usuwa nieużywane klasy i metody, ale także minifikuje nazwy w kompilacji release. Kompilacja z włączonym ProGuard automatycznie pokazuje, które klasy i metody są uznawane za nieużywane — w raporcie usage.txt wymieniony jest cały usunięty kod.

Analiza dynamiczna przez pokrycie testami

Narzędzia pokrycia kodu (JaCoCo dla Java, XCTest coverage dla Swift, Istanbul dla JavaScript) pokazują, które linie i gałęzie są wykonywane podczas testów. Metody z zerowym pokryciem to kandydaci na martwy kod. Jednak brak pokrycia nie gwarantuje, że kod nie jest wywoływany na produkcji — dla pełnej pewności używaj kombinacji analizy statycznej i dynamicznej.

Skonfiguruj CI-pipeline tak, aby kompilacja kończyła się niepowodzeniem po przekroczeniu progu nieużywanych deklaracji. SonarQube Quality Gate z regułą „Udział nieużywanego kodu prywatnego nie więcej niż 3%" zapobiega gromadzeniu się martwego kodu na poziomie procesu programistycznego.

Jak bezpiecznie usuwać martwy kod

Proces usuwania martwego kodu składa się z czterech kroków: znajdź, sprawdź, usuń, sprawdź ponownie. Pominięcie któregokolwiek kroku zwiększa ryzyko regresji.

Pierwszy krok — znajdź kandydatów za pomocą analizatora statycznego. Uzyskaj raport o nieużywanych deklaracjach: funkcjach, klasach, zmiennych, importach. Odfiltruj fałszywe alarmy — analizatory czasami się mylą przy refleksji, dynamicznym ładowaniu klas lub ukrytych wywołaniach przez serializację.

Drugi krok — sprawdź przez git blame i historię zmian. Sprawdź, kiedy i dlaczego kod został napisany. Jeśli kod był częścią funkcji wyłączonej przez feature toggle, upewnij się, że toggle jest utrwalony i nie zostanie włączony z powrotem. Zakomentuj kod, co do którego masz wątpliwości, i zostaw TODO z zgłoszeniem do ponownego sprawdzenia za miesiąc.

Trzeci krok — usuń w osobnej gałęzi z uruchomieniem pełnego zestawu testów. Jeśli testy przechodzą — prawdopodobieństwo regresji jest niskie. Jeśli testy padają — oznacza to, że kod jednak jest używany i trzeba ustalić, w jakim scenariuszu.

cpp
// przed — martwy kod i kod zombie w tym samym pliku
int calculateV1(int price) { // nigdzie nie wywoływane
    int tax = price * 0.18;
    return price + tax;
}

int calculateV2(int price, double rate) {
    return static_cast<int>(price * (1 + rate));
}

// po — martwy kod usunięty, kod zombie wyczyszczony
int calculatePrice(int price, double rate) {
    return static_cast<int>(price * (1 + rate));
}

Czwarty krok — code review zmian. Recenzent musi potwierdzić, że kod rzeczywiście jest martwy. Jeśli recenzent nie jest pewny — zostaw komentarz w kodzie i odłóż usunięcie do pełnej analizy. Po scaleniu gałęzi — usuń gałąź, aby nie mnożyć kodu zombie w repozytorium git.

Wprowadź zasadę: żaden pull request nie powinien zawierać nowego martwego kodu. Dodaj linter do hooków pre-commit, który blokuje commit w przypadku nieużywanych zmiennych lub importów. Profilaktyka jest zawsze tańsza niż czyszczenie.

Często zadawane pytania

Czy martwy kod może powodować błędy kompilacji?

Tak, jeśli martwy kod zawiera błędy składniowe lub odwołuje się do usuniętych typów. Współczesne kompilatory i tak sprawdzają martwe gałęzie, dlatego błąd w bloku if(false) spowoduje odmowę kompilacji. To zabezpieczenie: kod nie powinien być na tyle martwy, żeby kompilator go nie sprawdzał.

Jakie zagrożenie stanowi kod zombie dla nowicjuszy w zespole?

Kod zombie wprowadza w błąd: nowy programista widzi funkcję z dokumentacją i zakłada, że jest używana. Traci czas na studiowanie niedziałającego kodu i może przypadkowo powiązać nową logikę z przestarzałym bytem, co stworzy trudny do wyśledzenia błąd.

Jak znaleźć martwy kod w projekcie JavaScript?

Użyj ESLint z regułami no-unused-vars i no-unused-modules, a także narzędzia knip — analizuje ono exports i imports w całym projekcie, znajdując nieużywane pliki, funkcje i zależności. Dla dużych monorepozytoriów knip pokazuje najbardziej pełny obraz.

Czy warto usuwać martwy kod przed wydaniem?

Lepiej usuwać przed wydaniem, ale nie w ostatniej chwili. Usuwanie martwego kodu to praca techniczna, którą planuje się w sprincie osobno. Bezpośrednio przed wydaniem usunięcie może wprowadzić niestabilność, jeśli kod okazał się nie tak martwy, jak się wydawało.

Czy kompilatory pomagają automatycznie usuwać martwy kod?

Tak, współczesne kompilatory i minifikatory (ProGuard, R8, Terser, Closure Compiler) usuwają nieosiągalny kod na poziomie Dead Code Elimination. Nie zwalnia to jednak z konieczności czyszczenia źródeł: kompilator usuwa kod z pliku binarnego, ale nie z repozytorium — programiści nadal będą się na niego natykali podczas czytania.

Podsumowanie

  • Martwy kod — nieużywane fragmenty, które nigdy nie są wykonywane, ale pozostają w projekcie.
  • Kod zombie — podtyp martwego kodu, który działał wcześniej, ale stracił aktualność po refaktoryzacji.
  • Główne przyczyny powstawania: rozwój iteracyjny, feature toggle, autogeneracja i strach przed usunięciem.
  • Martwy kod zwiększa czas kompilacji, rozmiar pliku binarnego i obciążenie poznawcze zespołu.
  • Narzędzia wyszukiwania: SonarQube, ESLint, SwiftLint, pylint, vulture, knip, ProGuard, JaCoCo.
  • Bezpieczne usuwanie obejmuje: wyszukiwanie, analizę git, usunięcie w gałęzi, uruchomienie testów i code review.
  • Profilaktyka martwego kodu: lintery w CI, ostrzeganie o nieużywanym kodzie w code review i kultura refaktoryzacji.

Opracujemy aplikację mobilną pod klucz

IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.

Omów projekt

Przeczytaj również