Powrót do strony głównej

Współistnienie legacy-kodu w systemach high-load

W artykule omawiane są ryzyka usuwania legacy-kodu w dużych projektach i strategie współistnienia za pomocą feature toggles i fasad. Podane są przykłady na Spring Boot, schematy migracji ruchu i przypadki obowiązkowego usuwania. Podejście minimalizuje regresje, zachowując uptime.

Nie usuwaj legacy: feature toggles dla stabilności
Advertisement 728x90

Stabilność zamiast usuwania: strategie współistnienia kodu legacy w dużych projektach

W długowiecznych systemach o wysokim obciążeniu usunięcie kodu legacy wiąże się z ryzykiem regresji i utraty stabilności. Zamiast kompletnego refaktoryzowania lepiej stosować współistnienie starego i nowego kodu za pomocą feature toggles i fasad. Pozwala to rozwijać system bez przestojów, zachowując sprawdzone komponenty jako zapasowe.

Ryzyko agresywnego usuwania kodu

Usunięcie nawet martwego kodu może powodować awarie z powodu ukrytych zależności: reflection, dynamiczne ładowanie, wywołania RPC. Testy w systemach legacy rzadko obejmują przypadki graniczne, a dane produkcyjne aktywują rzadkie gałęzie. Utrata pamięci instytucjonalnej pogłębia problem — odchodząca drużyna zostawia kod krytyczny dla rzadkich scenariuszy, takich jak migracje czy zgodność z regulacjami.

W środowiskach enterprise niestandardowe moduły dostawców stają się narażone: zmiany blokują wsparcie producenta.

Google AdInline article slot

Zasada "nie ruszaj tego, co działa" w praktyce

Klasyczny przykład – NASA z sondą Deep Space 1: kod z lat 70. pozostał niezmieniony, otoczony adapterami. Stabilne komponenty nie są dotykane, jeśli spełniają swoje zadanie bez zakłóceń. To dojrzała strategia dla systemów starszych niż 5 lat.

Architektura współistnienia: Strangler Fig wstecz

Nie niszcz kodu legacy – buduj nowe równolegle, stopniowo przekazując ruch. Dwie strategie:

| Strategia | Działania | Warunki zastosowania |

Google AdInline article slot

|-----------|----------|-------------------|

| Usunięcie | Przepisać na nowym stacku | Prosta logika, 100% pokrycie testów, brak zależności |

| Współistnienie | Nowy kod równolegle, stopniowy routing | Krytyczna funkcjonalność, słabe testy, wysokie ryzyko |

Google AdInline article slot

Feature toggles zapewniają bezpieczeństwo:

  • Opracuj nową implementację.
  • Otocz w sprawdzanie flagi.
  • Uruchom na 1% ruchu.
  • Skaluj przy sukcesie.
  • Cofnij przez flagę przy awarii.

Kod legacy pozostaje w repozytorium 2–3 lata dla statystyki niezawodności.

Realizacja w Spring Boot

Otocz kod legacy interfejsem, nie zmieniając oryginalnego kodu.

// Legacy-serwis (nie dotykamy)
@Service
public class LegacyDiscountService {
    public double calculateDiscount(Order order) {
        // Logika z pięciu lat temu
        return order.getTotal() * 0.1;
    }
}

// Nowy serwis
@Service
public class NewDiscountService {
    public double calculateDiscount(Order order) {
        return applyComplexRules(order);
    }
}

Fasadę z flagą:

@Component
public class DiscountServiceFacade {
    private final LegacyDiscountService legacyService;
    private final NewDiscountService newService;
    
    @Value("${feature.discount.new.enabled:false}")
    private boolean useNewService;
    
    public DiscountServiceFacade(LegacyDiscountService legacyService, 
                                 NewDiscountService newService) {
        this.legacyService = legacyService;
        this.newService = newService;
    }
    
    public double calculateDiscount(Order order) {
        if (useNewService) {
            return newService.calculateDiscount(order);
        }
        return legacyService.calculateDiscount(order);
    }
}

Izolacja przez profile:

@Configuration
public class DiscountConfiguration {
    
    @Bean
    @ConditionalOnProperty(name = "feature.discount.new", havingValue = "false", matchIfMissing = true)
    public DiscountService legacyDiscountService() {
        return new LegacyDiscountService();
    }
    
    @Bean
    @ConditionalOnProperty(name = "feature.discount.new", havingValue = "true")
    public DiscountService newDiscountService() {
        return new NewDiscountService();
    }
}

Testy pomijają legacy:

@Test
@DisabledIf("!${feature.discount.new.enabled}")
void testNewDiscountLogic() {
    // Tylko nowa logika
}

Schemat przełączania ruchu

API Gateway routuje według flag: monolit jako fallback, mikroserwis stopniowo zwiększa obciążenie. Service zarządzający feature toggle kontroluje procenty w czasie rzeczywistym. Kod legacy w produkcji przez 2–3 lata.

Kiedy usunięcie jest nieuniknione

  • Bezpieczeństwo: CVE w usterzonych bibliotekach.
  • Zgodność: Naruszenia GDPR.
  • EOL technologii: Java bez aktualizacji.
  • Brak ekspertyzy: Brak specjalistów.

Nawet wtedy – stopniowy rollout z możliwością cofnięcia.

Co jest ważne

  • Stabilność systemu przeważa nad estetyką kodu: dostępność ma większą wartość niż usunięte linie legacy.
  • Ukryte zależności wymagają monitoringu wywołań w środowisku produkcyjnym przed usunięciem.
  • Feature toggles minimalizują ryzyko podczas migracji.
  • Dług techniczny się opłaca, jeśli utrzymanie jest tańsze niż refaktoryzacja.
  • Pamięć instytucjonalna w kodzie: nie usuwaj bez 100% pewności.

— Editorial Team

Advertisement 728x90

Czytaj dalej