Back to Home

Coexistence of legacy code in high-load systems

The article examines the risks of removing legacy code in large projects and coexistence strategies through feature toggles and facades. Examples on Spring Boot, traffic migration schemes and cases of mandatory deletion are provided. The approach minimizes regressions, maintaining uptime.

Don't delete legacy: feature toggles for stability
Advertisement 728x90

Stability Over Removal: Strategies for Legacy Code Coexistence in Large Projects

In long-running, high-load systems, deleting legacy code carries risks of regressions and instability. Instead of full-scale refactoring, it's smarter to let old and new code coexist using feature toggles and façades. This approach enables continuous development without downtime, keeping proven components as fallbacks.

Risks of Aggressive Code Removal

Even removing dead code can trigger failures due to hidden dependencies—reflection, dynamic loading, RPC calls. Legacy system tests rarely cover edge cases, and production data often activates rare execution paths. Loss of institutional knowledge worsens the issue: departing teams leave behind critical code for niche scenarios like migrations or compliance.

In enterprise environments, vendor-specific custom modules become vulnerable—changes can break vendor support.

Google AdInline article slot

The "Let Well Alone" Principle in Practice

A classic example is NASA’s Deep Space 1 probe: 1970s-era code remained untouched, wrapped in adapters. Stable components stay unchanged if they work reliably. This mature strategy suits systems older than five years.

Coexistence Architecture: The Strangler Fig in Reverse

Don’t destroy legacy—build the new system in parallel and gradually shift traffic. Two strategies:

| Strategy | Actions | When to Apply |

Google AdInline article slot

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

| Removal | Rewrite on a new stack | Simple logic, 100% test coverage, no dependencies |

| Coexistence | New code runs alongside, gradual routing | Critical functionality, weak tests, high risk |

Google AdInline article slot

Feature toggles ensure safety:

  • Develop the new implementation.
  • Wrap it in a flag check.
  • Run it on 1% of traffic.
  • Scale up if successful.
  • Roll back via flag if issues arise.

Legacy code stays in the repo for 2–3 years for reliability analytics.

Implementation in Spring Boot

Wrap legacy code in an interface without modifying the original.

// Legacy service (untouched)
@Service
public class LegacyDiscountService {
    public double calculateDiscount(Order order) {
        // Logic from 5 years ago
        return order.getTotal() * 0.1;
    }
}

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

Façade with toggle:

@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);
    }
}

Isolation via profiles:

@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();
    }
}

Tests exclude legacy:

@Test
@DisabledIf("!${feature.discount.new.enabled}")
void testNewDiscountLogic() {
    // Only new logic
}

Traffic Switching Workflow

API Gateway routes based on flags: monolith as fallback, microservice scales incrementally. Feature Toggle Service manages real-time percentages. Legacy remains in production for 2–3 years.

When Removal Is Unavoidable

  • Security: CVEs in outdated libraries.
  • Compliance: GDPR violations.
  • EOL tech: Java versions without patches.
  • No expertise: No specialists available.

Even then—use gradual rollout with rollback capability.

Key Takeaways

  • System stability trumps code aesthetics: uptime matters more than deleted legacy lines.
  • Hidden dependencies require monitoring actual production calls before deletion.
  • Feature toggles reduce migration risks.
  • Technical debt pays off if maintenance costs less than refactoring.
  • Institutional memory in code: don’t delete without 100% confidence.

— Editorial Team

Advertisement 728x90

Read Next