For those who check the comments before reading the article, here's a key piece of info from tfa:
During a vacation in the rain forests of Queensland in 2001, we saw some strangler figs. These are vines that germinate in a nook of a tree. As it grows, it draws nutrients from the host tree until it reaches the ground to grow roots and the canopy to get sunlight. It can then become self-sustaining, and its original host tree may die leaving the fig as an echo of its shape. This gradual process of replacing the host tree struck me as a striking analogy to the way I saw colleagues doing modernization of legacy software systems. A couple of years later I posted a brief blog post about this metaphor. While I've not used the term in my writing since then, it caught attention anyway, and the term “Strangler Fig” is now often used to describe a gradual approach to legacy modernization.
This topic always leads me to the ship of theseus wiki page https://en.m.wikipedia.org/wiki/Ship_of_Theseus and I always waste a couple of minutes thinking about which is the original ship, specially in the form where you replace all the pieces bit by bit, but keep the pieces and then put the old pieces back together.
I feel like it's a lot easier to think about in the My Grandfather's Axe form. The axe only has identity because you give it one and therefore it retains that identity through your possession of it.
If everyone who knew Theseus's name died, neither ship would ever be the Ship of Theseus again.
I’ve done strangler pattern for a large api and it worked well. I would like to use it for migrating a large Angular spa UI to Blazor. But that seems more daunting. Even if I did find a way to bridge and host Angular code inside the Blazor app, it just seems like it would be pretty complex. Has anyone done similar?
You may have some luck moving Angular components into Web Components first. Even if those Web Components are still using Angular inside at first, it possibly creates API boundaries that you can more easily bridge/have a better idea what to replace in your component rewrites.
Yeah, I looked into lit as a possible way to do this, but was left feeling like then I had 3 things to juggle - lit, angular, and blazor. Should possibly revisit, though.
Yeah lit and angular have very different approaches. I was thinking along the lines of @angular/elements [0] which is closer to a "lift and ship". It is a very heavy solution to building web components (especially compared to lit code golf), especially if you are going to pair it with Blazor wasm, but if you are already using Angular in a large enough application and looking toward the path to Blazor wasm, you probably are in a bandwidth situation where you can afford the transitional steps.
Bridging server side and client side rendering is going to be really tricky, and it may be fairly jarring from a UX perspective. Routing is going to be your biggest challenge, as the SPA will be effectively reinitialized when you route back to it if you keep it as-is.
I would try switching the SPA to render on the server entirely/for specific components if possible, knowing there will be angular code changes to support this, then do a progressive blazor rewrite of individual paths. Your app is going to run like hot garbage during this transition though, so you'll need plenty of buy-in to attempt this.
Well, in this case I'd be fine with client-side Blazor (WASM mode), as there is a mature REST API and it is an internal app where SEO etc is not important. But yes, routing is one hassle as well as the complexity for developers etc. We had this issue some when we did the api: ie say an issue was reported - developers would first have to figure out "is this a problem in apiv1 or apiv2, or in the way apiv2 proxies to apiv1?" Absolutely needs to be buy in that we're going to commit to finishing the migration in a reasonable time.
I still like that we did strangler pattern for the API vs the "rip the bandaid off" pattern, but it does carry some baggage that can be glossed over in some articles about it.
> Many believe that the mobile experience of the future will be centered around so-called “super-apps”; apps where you can pay, socialize, shop, call, message, and game, all under one application.
It's called a monopoly
> Comments from industry indicate a realization that the West is not quite as far along as China in this regard
If you're considering a similar pattern with Flutter rather than ReactNative, they call it "add to app" and there's a couple good talks on how others have approached this [1], [2] from the recent FlutterConUSA, as well as a couple articles that include details and case studies [3], [4]
I haven't tried this myself with a large project (just small examples as proof-of-concepts), but the approach seems very sound. One thing I liked is once you have the legacy app shell figured out, it's not a crazy approach to mock out the bridge/native services and run the app in just flutter (or react native) for development/testing acceleration, then adding final integration testing/QA with the full legacy app shell. I've seen some odd behaviors from apps that have used this approach that I would have to imagine can be serious headaches to debug. That said, it does seem the approach pays off long-term.
There's not much published online about it, but I believe Headspace has used this approach for its mobile app. See [5]
For those who check the comments before reading the article, here's a key piece of info from tfa:
During a vacation in the rain forests of Queensland in 2001, we saw some strangler figs. These are vines that germinate in a nook of a tree. As it grows, it draws nutrients from the host tree until it reaches the ground to grow roots and the canopy to get sunlight. It can then become self-sustaining, and its original host tree may die leaving the fig as an echo of its shape. This gradual process of replacing the host tree struck me as a striking analogy to the way I saw colleagues doing modernization of legacy software systems. A couple of years later I posted a brief blog post about this metaphor. While I've not used the term in my writing since then, it caught attention anyway, and the term “Strangler Fig” is now often used to describe a gradual approach to legacy modernization.
This topic always leads me to the ship of theseus wiki page https://en.m.wikipedia.org/wiki/Ship_of_Theseus and I always waste a couple of minutes thinking about which is the original ship, specially in the form where you replace all the pieces bit by bit, but keep the pieces and then put the old pieces back together.
I feel like it's a lot easier to think about in the My Grandfather's Axe form. The axe only has identity because you give it one and therefore it retains that identity through your possession of it.
If everyone who knew Theseus's name died, neither ship would ever be the Ship of Theseus again.
For the same reason, I drew this Snail of Theseus as the logo of my digital garden/lab notes: https://untested.sonnet.io/notes/the-snail-of-theseus/
I heard someone describe it as "The System of Theseus" that it's not an object but a set of things that lead to usefulness.
I’ve done strangler pattern for a large api and it worked well. I would like to use it for migrating a large Angular spa UI to Blazor. But that seems more daunting. Even if I did find a way to bridge and host Angular code inside the Blazor app, it just seems like it would be pretty complex. Has anyone done similar?
You may have some luck moving Angular components into Web Components first. Even if those Web Components are still using Angular inside at first, it possibly creates API boundaries that you can more easily bridge/have a better idea what to replace in your component rewrites.
Yeah, I looked into lit as a possible way to do this, but was left feeling like then I had 3 things to juggle - lit, angular, and blazor. Should possibly revisit, though.
Yeah lit and angular have very different approaches. I was thinking along the lines of @angular/elements [0] which is closer to a "lift and ship". It is a very heavy solution to building web components (especially compared to lit code golf), especially if you are going to pair it with Blazor wasm, but if you are already using Angular in a large enough application and looking toward the path to Blazor wasm, you probably are in a bandwidth situation where you can afford the transitional steps.
[0] https://angular.io/guide/elements
Bridging server side and client side rendering is going to be really tricky, and it may be fairly jarring from a UX perspective. Routing is going to be your biggest challenge, as the SPA will be effectively reinitialized when you route back to it if you keep it as-is.
I would try switching the SPA to render on the server entirely/for specific components if possible, knowing there will be angular code changes to support this, then do a progressive blazor rewrite of individual paths. Your app is going to run like hot garbage during this transition though, so you'll need plenty of buy-in to attempt this.
Well, in this case I'd be fine with client-side Blazor (WASM mode), as there is a mature REST API and it is an internal app where SEO etc is not important. But yes, routing is one hassle as well as the complexity for developers etc. We had this issue some when we did the api: ie say an issue was reported - developers would first have to figure out "is this a problem in apiv1 or apiv2, or in the way apiv2 proxies to apiv1?" Absolutely needs to be buy in that we're going to commit to finishing the migration in a reasonable time.
I still like that we did strangler pattern for the API vs the "rip the bandaid off" pattern, but it does carry some baggage that can be glossed over in some articles about it.
> Many believe that the mobile experience of the future will be centered around so-called “super-apps”; apps where you can pay, socialize, shop, call, message, and game, all under one application.
It's called a monopoly
> Comments from industry indicate a realization that the West is not quite as far along as China in this regard
Good!
If you're considering a similar pattern with Flutter rather than ReactNative, they call it "add to app" and there's a couple good talks on how others have approached this [1], [2] from the recent FlutterConUSA, as well as a couple articles that include details and case studies [3], [4]
I haven't tried this myself with a large project (just small examples as proof-of-concepts), but the approach seems very sound. One thing I liked is once you have the legacy app shell figured out, it's not a crazy approach to mock out the bridge/native services and run the app in just flutter (or react native) for development/testing acceleration, then adding final integration testing/QA with the full legacy app shell. I've seen some odd behaviors from apps that have used this approach that I would have to imagine can be serious headaches to debug. That said, it does seem the approach pays off long-term.
There's not much published online about it, but I believe Headspace has used this approach for its mobile app. See [5]
[1] https://www.droidcon.com/2024/10/17/flutter-add-to-app-the-g...
[2] https://www.droidcon.com/2024/10/17/successful-flutter-re-pl...
[3] https://docs.flutter.dev/add-to-app
[4] https://leancode.co/blog/flutter-add-to-app-overview-and-cha...
[5] https://www.nearcoast.com/headspaces-leap-to-flutter-a-game-...