Strangler Pattern Done Wrong

Some years ago, every backend engineer wanted to be doing microservices. There was absolute hype. A vast majority of the market joined the microservice bandwagon without previous expertise in SOA. Some companies were successful. Still, several companies ended up in the worst place they were before. Therefore we see the Death of Microservices. IMHO there are many reasons why that happens to name a few: Lack of proper architecture skills in SOA, Lack of Isolation, Lack of Analysis, and jump to solutions too fast. Once you start distributing code across several code bases with multiple contracts, it is better to be correct because the rollback is a multi-year migration project and not easy to sell. How does that kind of thing happen exactly? In Microservices, the way to go is to use the Strangler Pattern, however by applying the pattern in the wrong way, thats where we arrive at the worst outcome: A distributed monolith. 

How Wrong Strangler looks like?

The correct strangler pattern requires us to extract a common interface from the monolithic system and slowly move the source of truth to the new interface, however, if you dont the source of the truth, you are in a situation where you dont have the benefits of microservices, you have actually more problems to deal with such as:

 * You cannot change things in ISOLATION, coordination, and SYNCS need to happen

 * Code is distributed across different repositories, and refactoring and impact are much hard to figure out.

 * You cannot change some technologies(persistence path) without creating a big-bang migration

 * CI/CD becomes almost impossible, and then you need to have release trains/calendar 

 * Testing becomes harder, Troubleshooting is more complex, Debugging is Harder

The Strangler Pattern done wrong can seem in this 2 commons forms:

Basically, the wrong Strangler results up meaning: Different services using the same database. Either with a direct access form or with an indirect form via a shared lib. This gives a false illusion that these are different services when they are the same service. However, they are distributed and more complex. 

How to Fix for there? 

That's the hard part. 

We have multiple problems now, such as:

 * High maintenance cost (for microservices maintainers)

 * Difficult migrations, hard to do impact analysis, poor refactoring support (multiple git repos).

 * Binary Coupling, All or nothing situattion

 * Hard to implement cross features, way too many PRs, and cross-team coordination 

 * Syncs, release calendars/trains, high lead times

Some solutions might not fix all these problems but they might put you in a better direction and alleviate the pain and fix some problems at least. For instance, GraphQL, forget about BFFs and frontend for a moment. Think about a standard company-wide unified service, as a one-stop-shop for business access, in that scenario that might give you the abstractions needed to start isolating systems and making changes un the hood, as services, BFFS, UIs do not use services as much and see more/use more GraphQL. This does not fix all issues but might give you a runaway to start fixing the issues at the source. 

Recently I blogged about Strangler or Re-write. If possible, maybe it's easier to let things as they are and re-write them in a near/mid-term. It's also possible to apply Strangler done right and try to fix the distributed monolith. It's also possible to go back in the form of a monorepo approach and have the services in the same place, making refactoring easier and Maintainance. Does not fix the problem but modules living in the same place == modular monolith in a single deployment unit could be much easier to maintain. Another option could be to group services together like Uber did with DOMA. Whatever solution you pick, I recommend caution and careful analysis because, like murphy states, it always can be worst. 

In summary:

* GraphQL can help you to have a standard enterprise gateway, meaning the public contract you might need to allow changes under the hood. 

 * Consider doing Modular Monoliths with monorepos with a single deployment unit to minimize the mantaince cost and make refactoring and impact analysis cheap and much easier.

* If the bar is too far away from the barn consider Uber's DOMA approach, where DDD can be applied to reduce the complexity in cross-feature and consumers calls can be simplified. 

* IF the system is critical to the business consider using the Strangler Pattern in the right way. 

* IF you can afford or the business is growing in that direction, consider a re-write associate with new business capabilities in order to have a win-win scenario. 


Diego Pacheco

Popular posts from this blog

Kafka Streams with Java 15

Rust and Java Interoperability

HMAC in Java