Migrating away from legacy monoliths can feel like navigating a labyrinth, but strategic patterns like Strangler Fig, Branch by Abstraction, and Parallel Run simplify the journey. Here’s a quick dive into how they work, when to use them, and how to decide when to switch completely to the new implementation.
1. Strangler Fig Applications
- What It Is: Gradually replace monolith functionality by routing calls to new microservices. Over time, the monolith “withers” away.
- When to Use: When functionality is easily identifiable and can be routed externally using proxies.
- When to Switch: Once all functionality is migrated, no active dependencies remain, and the new system is stable.
- How to Switch: Remove routing rules to the old monolith and decommission the corresponding components.
2. Branch by Abstraction
- What It Is: Introduce an abstraction layer in the codebase to allow old and new implementations to coexist until the transition is complete.
- When to Use: When functionality is deeply coupled within the monolith, requiring internal refactoring to extract it.
- When to Switch: Once the new implementation meets functional and nonfunctional requirements, and bugs/inconsistencies are resolved.
- How to Switch: Activate the new implementation using feature toggles, remove the old implementation, and clean up the abstraction layer.
3. Parallel Run
- What It Is: Run old and new implementations side by side, comparing their outputs to validate the correctness of the new system.
- When to Use: For high-risk migrations where accuracy and performance must be verified before switching.
- When to Switch: Once the new implementation consistently produces equivalent results, meets performance thresholds, and discrepancies are resolved.
- How to Switch: Declare the new implementation as the source of truth and retire the old system.
Summary of Differences
Here’s a handy comparison table:
Aspect | Strangler Fig | Branch by Abstraction | Parallel Run |
---|---|---|---|
Focus | Incremental external replacement | Internal decoupling with abstractions | Simultaneous validation of old and new |
Transition Approach | Redirects calls to new service | Coexists via an abstraction layer in code | Calls both implementations in parallel |
Rollback | Easy (via redirection) | Via feature toggle | Retain old implementation as fallback |
When to Use | Clear routing path for migration | Tightly coupled functionality | High-risk migrations |
Challenges | Duplicated functionality | Code complexity, data consistency | Resource overhead, managing side effects |
Key Takeaways
- Use Strangler Fig for external routing and phased replacement of well-isolated functionality.
- Choose Branch by Abstraction when working with deeply embedded functionality that requires code refactoring.
- Opt for Parallel Run for high-risk, mission-critical transitions where correctness and performance must be validated.
Each of these patterns can complement one another as part of a cohesive migration strategy.