Strangler Fig — Incremental Legacy Replacement
Incrementally replace a legacy system by routing new traffic to a new implementation while the old one still runs.
When to use
- Migrating a monolith to services
- Replacing a legacy API too risky to rewrite in one shot
- Need to ship continuously during migration
Tradeoffs
- Two systems in production simultaneously (data sync complexity)
- The facade routing layer becomes critical path during migration
- Go
- Python
func router(legacyHandler, newHandler http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if isNewFeature(r) {
newHandler.ServeHTTP(w, r)
return
}
legacyHandler.ServeHTTP(w, r)
})
}
func isNewFeature(r *http.Request) bool {
return strings.HasPrefix(r.URL.Path, "/v2/")
}
from flask import Flask, request, Response
import httpx
app = Flask(__name__)
def is_new_feature(path: str) -> bool:
return path.startswith("/v2/")
@app.route("/<path:path>", methods=["GET", "POST", "PUT", "DELETE"])
def facade(path: str) -> Response:
target = "http://new-service" if is_new_feature(request.path) else "http://legacy"
resp = httpx.request(request.method, f"{target}/{path}")
return Response(resp.content, status=resp.status_code)
Gotcha: The facade IS the product during migration. Don't treat it as a throwaway — test it like production.