The Strangler Fig Pattern in ASP.NET Core: When to Use It and How
Modernize legacy .NET systems incrementally with zero downtime using YARP as the facade layer

Legacy .NET systems do not fail all at once β they accumulate risk gradually, one missed refactor at a time. Teams that try to modernize them in a single big-bang rewrite often find themselves six months in with a half-finished system that cannot be shipped and a production system that cannot be changed. The Strangler Fig pattern exists precisely for this situation: it lets you replace a legacy system one endpoint, one service, or one feature at a time, without ever stopping the clock.
If you want to see this pattern applied inside a full production-grade ASP.NET Core codebase β with every layer wired together and tested β the complete source code and worked examples are on Patreon, annotated and ready to run.
The pattern takes its name from the strangler fig tree, which grows around an existing host tree, gradually taking over its structure until the original tree can be removed entirely. Applied to software, the new ASP.NET Core system is built alongside the legacy application β first intercepting specific routes, then more, until the legacy system handles nothing and can be decommissioned.
What Problem Does the Strangler Fig Pattern Solve?
Enterprise .NET teams face a common dilemma: the legacy system is too risky to touch, but also too expensive to leave alone. It might be a classic ASP.NET Framework MVC application running on IIS, a .NET Framework Web API that predates dependency injection, or a monolith with tightly coupled business logic and no automated tests.
A full rewrite forces you to pause feature delivery, maintain two codebases in parallel under pressure, and go live with an untested replacement. Most big-bang rewrites fail β not because of technical incompetence, but because the risk accumulates and the deadline pressure shortcuts the quality.
The Strangler Fig pattern avoids this by making modernization a series of small, independently deployable steps. Each step ships. Each step reduces legacy surface area. The system is always in production and always functional.
How the Strangler Fig Pattern Works
The mechanics are straightforward: a facade layer sits in front of both the legacy and new systems. Every incoming request is intercepted at this facade. If the new system has a working implementation for that endpoint, the request is routed to the new system. If not, the request passes through to the legacy system unchanged.
The facade grows over time. You migrate one endpoint or one domain. You verify it works. You update the routing rules to stop forwarding that endpoint to legacy. You repeat. Over months β or years, for large systems β the legacy system handles progressively fewer requests until the routing table no longer contains any legacy routes and the legacy system is taken offline.
In the ASP.NET Core ecosystem, YARP (Yet Another Reverse Proxy) is the standard tool for implementing the facade. YARP is a Microsoft-maintained, ASP.NET Coreβnative reverse proxy that runs as standard middleware. It can route, transform, and load-balance requests based on route patterns, headers, or custom predicates β and it integrates with the rest of the ASP.NET Core pipeline cleanly.
When to Use It
The Strangler Fig pattern is the right choice when:
The legacy system is live and cannot afford downtime. If you cannot take the system offline, you cannot do a big-bang replacement. The Strangler Fig lets you migrate with zero planned downtime.
The legacy system has no adequate test coverage. Migrating piece by piece allows you to write tests for each piece as you move it. You cannot safely test a full rewrite of an untested system.
The business still needs features during migration. The new system can deliver new functionality while the migration continues in parallel. Feature delivery does not pause.
Teams are small and migration must be incremental. A two-person team cannot rewrite a 100K-line system in six months. They can migrate ten endpoints a sprint while shipping new features.
The technology stack needs to change, not just the architecture. Moving from ASP.NET Framework 4.x to ASP.NET Core, or from synchronous request handling to async, is far less risky when done one domain at a time.
When Not to Use It
When the legacy system is unmaintainable and tightly coupled. If a single endpoint touches forty stored procedures and three legacy services, migrating it in isolation is not straightforward. The Strangler Fig works best when domain boundaries can be drawn.
When the legacy system's data model is the actual problem. Migrating endpoints does not fix a fundamentally broken data schema. If the schema needs major restructuring, that work must run in parallel β and may require a different strategy (event-driven migration, dual-write patterns, or schema versioning).
When the team lacks operational maturity to run two systems simultaneously. Running legacy and new in parallel means two sets of deployments, two sets of monitoring, two sets of support. If your team cannot manage that operational overhead, the pattern adds more risk than it removes.
When the window for migration is very short. The Strangler Fig pattern is a long-game strategy. If you have three months and need the full system migrated, you may need a more aggressive approach β with higher risk.
Core Concepts
The Facade (YARP as the Router)
YARP acts as the strangler facade. It receives every inbound request and decides where to send it based on route configuration. Early in a migration, most routes point to the legacy system. As endpoints are migrated, their routes are updated to point to the new ASP.NET Core application.
YARP configuration can be code-based or file-based (appsettings.json). The key capability is route matching β you can match by path prefix, exact path, HTTP method, headers, or custom predicates. This granularity is what makes endpoint-by-endpoint migration practical.
A useful pattern is to use a YARP cluster named legacy that points to your on-premise IIS-hosted legacy application and a cluster named new-api that points to your new ASP.NET Core service. Each route entry specifies which cluster handles that request. Migrating an endpoint is as simple as changing one route's ClusterId from legacy to new-api β no downtime, just a config push.
Migrating Domain by Domain, Not Layer by Layer
The most common Strangler Fig mistake is trying to migrate infrastructure layers horizontally β "we'll migrate all controllers first, then the services, then the database access." This approach produces nothing deployable for months.
The correct strategy is vertical slicing: pick one complete domain β for example, product catalogue read operations β and migrate the full stack for that domain: the endpoint, the application logic, the data access, and the tests. Once that slice is working and routed through the new system, start the next slice. Each slice is independently valuable and independently deployable.
Dual-Write and Data Synchronisation
The most technically demanding aspect of a Strangler Fig migration is usually data. If the legacy and new systems share a database, you may be able to migrate endpoints without data changes initially β both systems read and write the same tables. If you want to move to a new schema or a new database in the new system, you need a dual-write period where changes are written to both stores and a reconciliation process keeps them in sync.
This is where the complexity genuinely lives. The Strangler Fig pattern does not prescribe how to solve data migration β that is a separate concern. But it is worth naming: you will need a data migration strategy, and the pattern works best when that strategy is planned before migration begins.
Feature Flags as a Traffic Controller
Feature flags can complement YARP routing in nuanced ways. Rather than hardcoding which cluster a route points to in YARP configuration, you can implement a custom YARP middleware that checks a feature flag per request. This allows you to:
- Route a percentage of traffic to the new system (canary release)
- Route traffic by tenant, user role, or region
- Roll back a specific endpoint to legacy without a deployment
ASP.NET Core's built-in feature management (Microsoft.FeatureManagement) integrates naturally with this approach.
Implementation Sketch
Setting up YARP as a strangler facade involves three key steps.
Step 1: Add YARP to your new ASP.NET Core host. YARP is available via the Yarp.ReverseProxy NuGet package and is registered as middleware in Program.cs alongside your new API routes. Both the new controllers and the YARP forwarder run in the same process.
Step 2: Configure routes and clusters. Your appsettings.json (or code-based configuration) maps route patterns to clusters. Migrated endpoints point to the new-api cluster. Everything else points to legacy. As you migrate, you move routes from one cluster to the other.
Step 3: Establish an observability boundary. Add structured logging and distributed tracing at the YARP middleware level. You need to know, for every request, which system handled it. This is non-negotiable β it is how you validate that legacy traffic is declining and new-system traffic is increasing as expected.
Trade-offs
| Trade-off | Detail |
|---|---|
| Operational complexity | Two systems in production simultaneously doubles deployment, monitoring, and support surface area |
| Route management discipline | The routing config is a critical artefact β it must be version-controlled, reviewed, and treated with the same rigour as application code |
| Data migration complexity | The pattern does not solve database migration β that needs a separate strategy |
| Long migration cycles | For large systems, migration may span years. Organisational commitment is required |
| Test coverage gap | Legacy code being forwarded through YARP has no new test coverage. The test gap only closes as endpoints are migrated |
The Circuit Breaker pattern integrates naturally with the facade layer β if the legacy system becomes unavailable, YARP can fail fast and the new system can return a graceful fallback response rather than timing out. For teams also working on ACL layers between domains, the Anti-Corruption Layer pattern is a direct complement to Strangler Fig when legacy domain models need translation before entering the new system. The Circuit Breaker pattern in ASP.NET Core covers the resilience side in detail.
Anti-Patterns to Avoid
Keeping the facade forever. The facade is temporary scaffolding. Teams that stop the migration partway through end up with a permanent proxy that adds latency and complexity without completing the goal. Decide upfront what "done" looks like and treat it as a hard milestone.
Migrating without feature parity validation. Before updating a route in YARP to point to the new system, run both systems in parallel on the same request shape and compare responses (shadow testing or traffic mirroring). Skipping this step leads to silent regressions.
Migrating only the happy path. Legacy systems often have years of edge-case handling baked in β error responses, content negotiation quirks, unusual header behaviour. Your new implementation needs to match that behaviour, not just the success path.
Treating data migration as an afterthought. Many migrations stall because the endpoint logic is migrated but the database is not. Plan data migration upfront, even if execution is deferred.
Letting the legacy codebase continue to grow during migration. Every new feature added to the legacy system is another thing to migrate. If migration is underway, new feature work should go into the new system β even if the facade is not yet routing that domain.
β Prefer a one-time tip? Buy us a coffee β every bit helps keep the content coming!
FAQ
What is the Strangler Fig pattern in ASP.NET Core? The Strangler Fig pattern is an incremental migration strategy where a new ASP.NET Core system is built alongside a legacy application. A reverse proxy facade (typically YARP in .NET) intercepts all requests and routes each one to either the new or legacy system based on which endpoints have been migrated. The legacy system is decommissioned once all routes have been moved to the new system.
When should I use the Strangler Fig pattern instead of a full rewrite? Use it when your legacy system is live and cannot afford downtime, when the team is small and migration must happen alongside feature delivery, when the legacy codebase lacks test coverage, or when risk tolerance is low. A full rewrite is appropriate only when the legacy system is truly unmaintainable and all stakeholders accept the risk of a big-bang cutover.
How does YARP fit into the Strangler Fig pattern in .NET? YARP (Yet Another Reverse Proxy) is a Microsoft-maintained ASP.NET Core library that acts as the strangler facade. It runs as middleware in an ASP.NET Core host and routes incoming requests to either the legacy cluster or the new API cluster based on configurable route rules. Migrating an endpoint from legacy to new is as simple as updating a route's cluster target in YARP configuration β no code change required.
How do I handle database migration with the Strangler Fig pattern? The pattern does not prescribe a data migration strategy. Common approaches include sharing a single database between legacy and new systems during migration (simplest, works when the schema is not changing), dual-write with synchronisation (required when the new system uses a different schema), and event-driven migration using the outbox pattern to propagate changes between stores. Plan data migration before starting endpoint migration β it is almost always the most complex part.
Can I use feature flags with the Strangler Fig pattern in ASP.NET Core?
Yes. Custom YARP middleware can evaluate feature flags (via Microsoft.FeatureManagement) to decide which cluster a request is forwarded to. This enables canary releases (route 5% of traffic to the new system), tenant-based routing, and instant rollback without a deployment β just flip the flag.
How do I validate that the new system behaves identically to the legacy system? Shadow testing (traffic mirroring) is the most reliable approach. Route production traffic to both systems simultaneously, compare responses, and log discrepancies β without affecting the user. YARP supports request duplication via custom middleware. This should be done before updating any route to actively serve users from the new system.
What is the typical timeline for a Strangler Fig migration? There is no universal answer β it depends on the size of the legacy system, team capacity, and how cleanly the domain boundaries can be drawn. Small APIs (20β50 endpoints) can be migrated in a few months. Large monoliths may take 12β24 months. The key is to keep migration slices small, ship each one independently, and maintain organisational commitment for the full duration.




