Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Killing a Giant - a Practical Guide Through the Martin Fowler's Strangler Fig Pattern

Killing a Giant - a Practical Guide Through the Martin Fowler's Strangler Fig Pattern

Back in 2019, our company was preparing for a period of fast growth. One of the key blockers to that growth was a monolithic application called Accounts. Built initially around 2014 as a rapidly developed proof of concept, it quickly became a central piece for the customer interaction, a billing system, an auth server, a support ticketing system, the project lifecycle management system. The engineering debt grew exponentially with every new feature added. The system needed to be replaced.

Martin Fowler described an interesting solution for a practically zero-downtime migration project from a monolithic application to -- something else. Instead of replacing an app with a single big bang, let’s build the new application around the existing one, and let them slowly take over its responsibilities until we’re ready to just delete it entirely. The concept was stolen from a natural phenomenon of Australian strangler figs growing around the host tree until they kill it.

What could possibly go wrong with such an approach, you may ask yourself. Well -- as we learned in the last couple of years -- quite a lot of things! To name a few: shared state between the legacy and the replacement application, designing the stopgap communication between the applications, balancing the development of the new features with the migration of the existing ones.

Join me for the session where we’ll discuss the theory and practice of the Strangler Vine Pattern around a Drupal 7 monolith, with a special focus on all the embarrassing errors we made along the way.

Branislav Bujisic

June 25, 2022
Tweet

More Decks by Branislav Bujisic

Other Decks in Technology

Transcript

  1. Drupal 7 monolith Billing Subscription management Payments Dunning User management

    Oauth2 server Support ticket management UX Identity provider Trials Reporting (BI) Perfectly extendable to support a rapidly growing startup
  2. Design stamina hypothesis “Is it worth the effort to design

    software well?” - Martin Fowler 2007
  3. Drupal 7 becomes a huge source of technical debt over

    the years because: + Abuse of the hook system + Uncontrollable changes of the object state + Mutually dependent code (circular dependencies!!!), contrib/custom code hell, patches... + Configuration, content and code, managed inconsistently in code or in databases; difficult revisioning etc. + Infrastructure stuck at old version of PHP Technical debt
  4. A cunning plan: microservices Billing Subscription management Payments Dunning User

    management Oauth2 server Support ticket management UX Identity provider Trials Reporting (BI)
  5. Replacing a huge legacy system may be problematic… + Designing

    a system from the ground up in a holistic way is ridiculously complicated + The data is too complex to be transformed and migrated in a single act + There is a lot of risk in releasing overwhelming changes in a big bang! + A down time must be avoided at all costs
  6. [...] gradually create a new system around the edges of

    the old, letting it grow slowly over several years until the old system is strangled. Martin Fowler, 2004
  7. Phases of the Strangler Application Transform Create a new site

    Eliminate Remove functionality from the old site. Phase 1: Phase 2: Phase 3: Coexist Leave the existing site. As a new feature was built, redirect to the new site.
  8. Phases of the Strangler Application 2nd iteration Build a service

    …and another service Phase 1: Phase 2: Phase 3: Phase 4: Create the new service Add a proxy Build another service Proxy to the new service Iterate until perfect Clean up the monolith …and another Phase 4:
  9. Phase 1: Transform The Proxy The New Service /feature/(.*) The

    Legacy App /(.*) + First create a proxy between the application and the users. The proxy does not do anything other than passing all the traffic to the application. + Start building the replacement application in the background. Ensure it looks and behaves the same as the legacy application. RewriteEngine On # Serve feature from new service for internal network RewriteCond expr “%{HTTP:X-FORWARDED-FOR} -ipmatch ‘192.168.0.1/24’” RewriteRule ^/feature/(.*)$ ${NEW_SERVICE_URL}/$1 [P,L] # Proxy everything else to legacy application RewriteRule ^/(.*) ${LEGACY_URL}/$1 [P] ProxyPassReverse / ${LEGACY_URL}/
  10. Phase 2: Coexist + Proxy requests to the new service

    as soon as it’s done. + Serve the rest from the legacy application. + Start building another replacement application in the background. The Newer Service /other-feature/(.*) The Proxy The New Service /feature/(.*) The Legacy App /(.*)
  11. Phase 3: Eliminate + After all the features have been

    built in the replacement application(s), the legacy app serves no more traffic. + Switch off the legacy app. The Proxy The Legacy App /(.*) The Newer Service /other-feature/(.*) The Proxy The New Service /feature/(.*) The Legacy App Orphaned service The Last Service /last-feature/(.*) …
  12. The strengths • True zero downtime • Reduced risk of

    failures due to frequent small releases • Faster delivery of the value for customers • Lesser cognitive load for the engineering team
  13. The opportunities • Data driven refactoring + The proxy is

    a central place to collect the customer requests, log them and allow their analysis to enable prioritization of the refactoring, as well as fixing broken user experience. • Decoupled frontend + Deciding for the proxy to be exclusively an API proxy allows the frontend engineering team to grow independently; to centralize the API documentation; and provides immediate value to the customers. • Mutually independent teams and choice of the best tool for the job + Microservices allow using different technologies inside each one, thus avoiding the one-size-fits-all approach.
  14. The result is beautiful and it makes Total Sense™ Billing

    Subscription management User management Support ticket management UX Trials Reporting (BI) API Proxy + Single unified UX for great customer experience. + API proxy capable of load balancing if needed. + Segregated services, each with own tech stack and engineering team.
  15. User data migration 2h Weakness: data migration Legacy App The

    Proxy Auth Service Problem: Inconsistent state during the migration. Where to proxy the traffic? Solution: Close for maintenance — Problem: Long downtime Solution: Data migration strategy which does the most of the work in background — Problem: Rollback strategy in case of failure? Solution: Snapshots Solution: Synchronization back to Legacy App?
  16. Weakness: shared state Legacy App The Proxy Auth Service Subscription

    mgmt User management Billing A customer Company Name Address Tax ID Email An owner An ID Address country A user Email Password Address country An ID Problem: Services will be relying on the same data! How to synchronize it between them? Solution: Use the existing restful APIs to GET and PATCH when needed Keeping services in sync…
  17. Weakness: shared state Billing Subscription management User management Support ticket

    management Trials Reporting (BI) Problem: Lots of point to point API communication that bring to tight coupling. Solution: Message broker — Problem: Problem of deletion of an entity in a service that then needs to notify all the other services of it. Solution: Tombstones Solution: Message broker — Problem: Message brokers also need engineering – both for development and maintenance
  18. Threat: fragmentation of entity data Problem: “Store only what you

    will use” Problem: Business intelligence depends on reports SELECT * FROM users WHERE uid = 123 Solution: Data lake (e.g. fivetran + big query) Solution: Event sourcing? The User Entity from Drupal 7 Legacy App The Proxy Auth Service Subscription mgmt User management An owner Billing A customer A user Company Name Address Tax ID Email An ID Email Password Address country An ID Address country
  19. Threat: a new feature Legacy App The Proxy Subscription mgmt

    Billing Subscription management Usage records WIP ? Things to consider when deciding where to build a newly requested feature: + The state of the WIP service and the minimal change necessary to make the new feature + The size of the new feature and the amount of engineering debt that would be created if implemented in the legacy application + The urgency of the new feature New product line
  20. Threat: keeping backward compatibility (At Any Cost™) POST /provision POST

    /orgs/{id}/provision POST /provision The Proxy Legacy App Subscription management GET /orgs if org # = 0: POST /orgs if org # > 1: Throw Exception Provision project POST /orgs/{id}/stopgap-api
  21. Other weaknesses and threats • Heavy on engineering resources +

    Maintenance overhead for each service + Engineers specialize + Silos within the team + Tiring joggling between building new and rebuilding old features • Requires real good monitoring + Health of the each of the services + X-service debugging • Fundamental changes break processes in other teams + CSEs, TAMs, even sales need to be onboarded + Major overhaul of internal and external documentation