Skip to main content
pragmatic clean code minimizing cognitive load in production java

The Strangler Fig Pattern

4 min read Chapter 21 of 25
Summary

The Strangler Fig pattern migrates legacy code incrementally...

The Strangler Fig pattern migrates legacy code incrementally using interface shims and three phases: Transform, Coexist, Eliminate. It reduces risk, enables dark launching for testing, and connects to local reasoning and technical debt management. The pattern leverages clear seams and feature flags to switch implementations.

The Strangler Fig Pattern: A Pragmatic Approach to Migration

Maintenance costs dominate the software lifecycle, accounting for over 70% of total expenditure. The Strangler Fig pattern, coined by Martin Fowler, offers a low-risk migration strategy for monolithic components, enabling developers to gradually replace legacy code with new implementations. This approach is crucial, given that engineers spend 60-90% of their time reading and understanding code, with a read-to-write ratio of approximately 10:1.

Understanding the Strangler Fig Pattern

The Strangler Fig pattern involves three phases: Transform, Coexist, and Eliminate. The Transform phase entails building a new implementation, while the Coexist phase introduces a shim that routes calls to either the legacy or new implementation. The Eliminate phase removes the legacy code, completing the migration. This process relies on identifying a clear ‘seam’ where requests can be intercepted, allowing for the gradual replacement of monolithic components.

Finding the Seam

Implementation leakage occurs when internal mechanics are visible in a module’s public interface, hindering strangler efforts. To find a suitable seam, developers should look for areas where the interface is not tightly coupled to the implementation, enabling the introduction of a shim without affecting the external behavior. The shim must implement the same interface as the legacy class to be transparent to callers.

The Role of Shims in Migration

Shims play a vital role in the Strangler Fig pattern, enabling the coexistence of legacy and new implementations. By implementing the same interface as the legacy class, shims can intercept calls and redirect them to either the old or new implementation. This allows for ‘Dark Launching,’ where both old and new implementations run in parallel, facilitating behavior-focused testing and comparison of results.

Example: Java Interface Shim

public interface PaymentProcessor {
    void process(double amount);
}

// 1. Legacy Implementation
public class LegacyPaymentProcessor implements PaymentProcessor {
    public void process(double amount) {
        System.out.println("Processing via old SOAP service...");
    }
}

// 2. New Implementation
public class ModernPaymentProcessor implements PaymentProcessor {
    public void process(double amount) {
        System.out.println("Processing via new REST API...");
    }
}

// 3. The Strangler Shim
public class PaymentStranglerShim implements PaymentProcessor {
    private final LegacyPaymentProcessor legacy = new LegacyPaymentProcessor();
    private final ModernPaymentProcessor modern = new ModernPaymentProcessor();
    private boolean useModern = false; // Controlled by config/feature flag

    public void setUseModern(boolean useModern) { this.useModern = useModern; }

    @Override
    public void process(double amount) {
        if (useModern) {
            modern.process(amount);
        } else {
            legacy.process(amount);
        }
    }
}

This Java example demonstrates an interface shim used to switch implementations gradually. The shim implements the PaymentProcessor interface, allowing it to intercept calls and redirect them to either the legacy or modern implementation, controlled by a feature flag.

Connecting to Prior Concepts

The Strangler Fig pattern is closely related to the concept of Local Reasoning, which enables developers to understand a module or function by looking only at its implementation and immediate context. By keeping the ‘happy path’ at the lowest level of indentation, developers can enhance local reasoning, making it easier to maintain and refactor code. Additionally, the pattern allows for the incremental payment of Technical Debt, as the new implementation can be built and understood in isolation, reducing the complexity of the overall system.

Conclusion

The Strangler Fig pattern offers a pragmatic approach to migrating monolithic components, enabling developers to gradually replace legacy code with new implementations. By understanding the three-phase process, identifying suitable seams, and utilizing shims, developers can reduce maintenance costs and improve the overall quality of their software systems. As maintenance costs continue to dominate the software lifecycle, the Strangler Fig pattern provides a valuable tool for developers seeking to improve the maintainability and scalability of their systems.

Sources