quinta-feira, novembro 21, 2024
HomeSoftware DevelopmentUsing the Strangler Fig with Mobile Apps

Using the Strangler Fig with Mobile Apps


In this article we aim to show why taking an incremental approach to
legacy mobile application modernization can be preferable to the classical
‘rewrite from scratch’ methodology. Thoughtworks has the benefit of working with
large enterprise clients that are dependent on their in-house mobile
applications for their core business. We see many of them asking their
applications to do more and evolve faster, while at the same time, we see an
increasing rejection of reputationally damaging high risk releases.

As a solution, this article proposes alternative methods of legacy
modernization that are based in Domain Driven Design and hinge on the
application of the Strangler Fig pattern. While these concepts are far from
new, we believe that their usage in mobile applications are novel. We feel
that despite incurring a larger temporary overhead from their usage, this is
an acceptable tradeoff. We assert how the methodology is used to combat the
aforementioned attitudinal shifts in legacy mobile application development
while gaining a platform to lower risk and drive incremental value
delivery.

We discuss how this works in theory, diving into both the architecture
and code. We also recount how this worked in practice when it was trialled on
a large, legacy mobile application at one of Thoughtworks’ enterprise
clients. We highlight how the pattern enabled our client to rapidly build,
test and productionize a modernized subset of domain functionalities inside
an existing legacy application.

We move on to evaluate the effectiveness of the trial by highlighting the business
facing benefits such as a signficantly faster time to value and a 50% reduced median cycle
time. We also touch on other expected benefits that should be used to
measure the success of this methodology.

The Problem with Mobile Legacy Modernization

As applications age and grow, they tend to deteriorate both in quality
and performance. Features take longer to get to market while outages
and rolled back releases become more severe and frequent. There is a
nuanced complexity to be understood about the reasons why this
occurs both at the code and organizational level.
To summarize though, at some point, an
organization will grow tired of the poor outcomes from their
software and start the process of legacy replacement. The decision
to replace may be made based on multiple factors, including (but not limited to)
cost/benefit analysis, risk analysis, or opportunity cost. Eventually a legacy modernization strategy will be chosen.
This will be dependent on the organization’s attitude to risk. For
example, a complex, high availability system may demand a more
incremental or interstitial approach to legacy
replacement/displacement than a simpler, less business critical one.

In the case of mobile application modernization, those decisions have
in recent memory been reasonably clear cut. A mobile application was
often designed to do an individual thing- Apple’s “There’s an app for
that” still rings out loud and clear in people’s minds 15 years after
the initial batch of advertisements. That message was one that was taken
to heart by organizations and startups alike: If you need to do
something, write an app to do it. If you need to do something else, write
another app to do that.
This example struck me when I was
pruning the apps on my phone a couple of years ago. At the time I noticed I
had several apps from the manufacturer of my car; an older one and a newer
one. I also had two apps from my bank; one showed my checking account,
another that analyzed and illustrated my spending habits. I had three apps
from Samsung for various IoT devices, and at least two from Philips that
controlled my toothbrush and light bulbs. The point I’m laboring here is
that a mobile application was never allowed to get so complicated,
that it couldn’t be torn down, split out or started from scratch again.

But what happens when this isn’t the case? Surely not all apps are
created equal? 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. To some degree this has
already happened in China with “do-everything” applications like
‘WeChat’ and ‘AliPay’- we see the mobile device and its operating
system as more of a vehicle to allow the running of these gigantic
pieces of software. Comments from industry indicate a realization
that the West
is not quite as far along as China in this regard
. But while not
at the super-app, there is no doubt that complexity of the mobile
app experience as a whole has increased significantly in recent
years. Take the example of YouTube, when first installed, back in
the early 2010’s, the application could play videos and not much
else. Opening the application today one is presented with “Videos”
and “Shorts”, a news feed, controllable categories, subscriptions,
not to mention a content editing and publishing studio. Similarly
with the Uber app, the user is asked if they want to order food.
Google Maps can show a 3D view of a street and Amazon now recommends
scrollable product-recommendation mood boards. These extra features
have certainly enriched a user’s experience but they also make the
traditional build, use, rebuild technique much more difficult.

This difficulty can be explained by considering some of the existing
common problems of mobile application development:

  • Massive View Controllers/Activities/Fragments
  • Direct manipulation of UI elements
  • Platform specific code
  • Poor Separation of Concerns
  • Limited Testability

With discipline, these problems can be managed early on. However, with
a large application that has grown chaotically inline with the business it
supports, incremental change will be difficult regardless. The solution then, as
before, is to build new and release all at once. But what if you only want
to add a new feature, or modernize an existing domain? What if you want to
test your new feature with a small group of users ahead of time while
serving everyone else the old experience? What if you’re happy with your
app store reviews and don’t want to risk impacting them?

Taking an incremental approach to app replacement then is the key to
avoiding the pitfalls associated with ‘big bang releases’. The Strangler
Fig pattern
is often used to rebuild a legacy application in
place: a new system is gradually created around the edges of an old
one through frequent releases. This pattern is well known, but
not widely used in a mobile context. We believe the reason for this is that there are several prerequisites that need to be in
place before diving headfirst into the pattern.

In their article on Patterns
of Legacy Displacement
, the authors describe four broad
categories (prerequisites) used to help break a legacy problem into
smaller, deliverable parts:

  1. Understand the outcomes you want to achieve
  2. Decide how to break the problem up into smaller parts
  3. Successfully deliver the parts
  4. Change the organization to allow this to happen on an ongoing
    basis

Only in the third point, can we envisage the invocation of the Strangler Fig
pattern. Doing so without an understanding of why, what or how it might
continue in the future is a recipe for failure.

Going forward, the article charts how Thoughtworks was able to help one
of its enterprise clients expand its existing mobile legacy modernization
efforts into a successful experiment that demonstrated the value behind
the use of the Strangler Fig pattern in a mobile context.

Satisfying the Prerequisites

At this point, it seems appropriate to introduce the client that
inspired the writing of this article – a globally distributed business
with an established retail organization that had embraced mobile
applications for many years. Our client had realized the benefits an
app brought to provide a self-service experience for their
products. They had quickly expanded and developed their app domains to allow millions
of customers to take full advantage of all the products they sold.

The organization had already spent a significant amount of time and
effort modernizing its mobile applications in its smaller
sub-brands. Responding to a lack of reuse/significant duplication of
efforts, high
cognitive load
in app teams and slow feature delivery, the
organization chose a mobile technology stack that leveraged a
Modular Micro-app architecture. This strategy had been largely
successful for them, enabling proliferation of features common to
the organization (e.g. ‘login/registration/auth’ or ‘grocery shopping’)
across different brands and territories, in a fraction of the time it
would have taken to write them all individually.

The diagram above is a simplified representation of the modular
architecture the organization had successfully implemented. React
Native was used due to its ability to entirely encapsulate a
domain’s bounded context within an importable component. Each
component was underpinned by its own backend
for frontend (BFF)
that came with the infrastructure as code to
instantiate and run it. The host apps, shown above as UK and US,
were simply containers that provided the app specific configuration
and theming to the individual micro-apps. This ‘full slice’ of
functionality has the advantages of both allowing re-use and
reducing complexity by abstracting application domains to micro-apps
managed by individual teams. We speak in depth about the results of
this architecture in the already referenced article on ‘Linking
Modular Architecture’
.

As touched upon earlier, the organization’s mobile estate was made up of
a number of smaller sub-brands that served similar products in other
territories. With the modular architecture pattern tried and tested, the
organization wanted to focus efforts on its ‘home-territory’ mobile
application (serving its main brand). Their main mobile app was much
larger in terms of feature richness, revenue and user volumes to that of
the sub brands. The app had been gaining features and users over many
years of product development. This steady but significant growth had
brought success in terms of how well-regarded their software was on both
Google and Apple stores. However, it also started to show the
characteristic signs of deterioration. Change frequency in the application
had moved from days to months, resulting in a large product backlog and
frustrated stakeholders who wanted an application that could evolve as
fast as their products did. Their long release cycle was related to risk
aversion: Any outage in the application was a serious loss of revenue to
the organization and also caused their customers distress due to the
essential nature of the products they sold. Changes were always tested
exhaustively before being put live.

The organization first considered a rewrite of the entire application
and were shocked by the cost and duration of such a project. The potential
negative reception of a ‘big bang’ new release to their app store
customers also caused concerns in the levels of risk they could accept.
Suggestions of alpha and beta user groups were considered unacceptable
given the huge volumes of users the organization was serving. In this
instance, a modernization effort similar to that seen in their sub-brands
was believed to be of considerably higher cost and risk.

Thoughtworks suggested an initial proof of concept that built on the
successes of the reusability already seen with a modular
architecture. We addressed the organization’s big bang risk aversion
by suggesting the Strangler
Fig pattern
to incrementally replace individual domains. By
leveraging both techniques together we were able to give the
organization the ability to reuse production-ready domains from
their modernized mobile apps inside their legacy app experience. The
idea was to deliver value into the hands of customers much sooner
with less duplication than in a full rewrite. Our focus was not on
delivering the most beautiful or cohesive full app experience (-not
quite yet anyway). It was about obtaining confidence both in the
stability of the iterative replacement pattern and also in how well
the new product was being received. These pieces of information
allowed the organization to make more informed product decisions
early on in the modernization process. This ensured the finished product
had been extensively used and molded by the actual end users.

Strangler Fig and Micro-apps

So how far did we get with the proof of concept and more importantly
how did we actually do this? Taking the learnings from Modular Micro-app
architecture (described above), we theorized the design to be as follows:

The initial state of the application involved the identification of
domains and their navigation routes (Decide how to break the problem into
smaller parts)
. We focused our efforts on finding navigation entry points
to domains, we called them our ‘points of interception’. Those familiar
with mobile application development will know that navigation is generally
a well encapsulated concern, meaning that we could be confident that we
could always direct our users to the experience of our choosing.

Once we identified our ‘points of interception’, we selected a domain
for incremental replacement/retirement. In the example above we focus on
the Grocery domain within the existing application. The ‘new‘ Grocery domain,
was a micro-app that was already being used within the sub-brand apps. The
key to implementation of the Strangler Fig pattern involved embedding an
entire React Native application inside the existing legacy application.
The team took the opportunity to follow the good modularity practices that
the framework encourages and built Grocery as an encapsulated component. This
meant that as we added more domains to our Strangler Fig Embedded
Application, we could control their enablement on an individual level.

As per the diagram, in the legacy app, Grocery functionality was
underpinned by a monolithic backend. When we imported the New Grocery
Micro-app, it was configured to use that same monolithic backend. As
mentioned previously, each micro-app came with its own Backend for
Frontend (BFF). In this instance, the BFF was used as an anti-corruption
layer; creating an isolating layer to maintain the same domain model as
the frontend. The BFF talked to the existing monolith through the same
interfaces the legacy mobile application did. Translation between both
monolith and micro-app happened in both directions as necessary. This
allowed the new module’s frontend not to be constrained by the legacy API
as it developed.

We continued the inside out replacement of the old application by
repeating the process again on the next prioritized domain. Although out
of scope for this proof of concept, the intention was that the process
shown be repeated until the native application is eventually just a shell
containing the new React Native application. This then would allow the removal of the
old native application entirely, leaving the new one in its place. The new
application is already tested with the existing customer base, the
business has confidence in its resilience under load, developers find it
easier to develop features and most importantly, unacceptable risks
associated with a typical big bang release were negated.

We’re releasing this article in installments. Future installments will
go into more detail on how we implanted the strangler fig, handled
communication with the native application, and assessed the
effectiveness of the new architecture..

To find out when we publish the next installment subscribe to this
site’s
RSS feed, or Martin’s feeds on
Mastodon,
LinkedIn, or
X (Twitter).




RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

- Advertisment -
Google search engine

Most Popular

Recent Comments