Example of a failed Micro-Services project and why Monoliths are valid

An excellent article on the perils of moving to Micro-services from a failed real world project is here.   It is simply a myth that ‘all firms’, and ‘all projects’, ‘must’ move to a Micro-Services ‘model’ of development and deployment.  That is lunacy.  Monoliths are completely valid in the proper context of domain, business logic, functionality, number of end-users (stable, not that variable), skills, budget and time.  The key highlights from the article outlining the failure to move to Micro-Services includes the following.

===

Cults vs reality

We were using monolith like a loaded term. As if saying “monolith” implies something terrible, and “microservices” implies something good. Once we looked past the stereotypes and branding, the development team had very few issues with our “monolith.”

No paint points to resolve

(With the Monolith) We had an excellent CI/CD setup, which made it easy to deploy and rollback. Our branching and testing strategies ensured that few issues that made it into production.

We didn’t have a list of our pain points, and we had no clear understanding of how this would help solve any pain points we do have. Worse, microservices might be just about to create a whole set of new problems for us.

Big Bang approach

Our plan to get to microservices was a big bang. Everyone stops feature work for a couple of months and starts splitting up our monolith. Even though many of the prerequisites weren’t ready. We were forcing the way ahead rather than waiting for either the need to arise or natural candidates to emerge.

Not only was this not a very good way from getting from a to b, but it was also backwards. Create all of the microservices first, then set up the infrastructure for them and completely ignore the aspects structuring the teams and incoming work. Instead, if we had started by restructuring our teams around dedicated business concerns, then gotten the infrastructure ready, we set the stage for microservices to naturally emerge. If any new business concerns emerged, they could be placed directly into a new service.

No experience, tooling, standards

Compounding the risks and time pressures, none of the people responsible for architecting or implementing the microservices architecture had any specific prior experience. This was exacerbated by not having a lot of the standard tooling ready to use, meaning we would be implementing the platform ourselves.

No TOM

It wasn’t uncommon for requirements to change mid feature. This uncertainty made creating microservices more fraught, as we couldn’t predict what new links would pop up, even in the short term. Would the connections and coupling between the planned microservices grow?

For example, we would need to address or revisit: logging, monitoring, exception handling, fault tolerance, fallbacks, microservice to microservice communication, message formats, containerization, service discovery, backups, telemetry, alerts, tracing, build pipelines, release pipelines, tooling, sharing infrastructure code, documentation, scaling, timezone support, staged rollouts, API versioning, network latency, health checks, load balancing, CDC testing, fault tolerance, debugging and developing multiple microservices in our local development environment.

To make matters worse, without a microservices platform ready, we would have to be working a lot of the above list out for ourselves.

Domain issues

For example, our application occasionally has to convert features between domains. Making one part of the third-party’s domain act and feel like it was part of a different domain in our UI. This swap was not so bad when we had a single service between our front end and the third-party. However, the domain switching caused us much confusion when we tried to split our domains into separate microservices.

Business Logic cannot be decoupled

We couldn’t identify any obvious candidates in our monolith to be broken out into a microservice. So instead, we started drawing arbitrary lines between our domain models, and from this, we had the list of microservices we were to create. However, once we started investigating, we found a lot of shared business logic and implicit coupling between the soon to be separate microservice domains.

Micro-Services and Domains

Somewhere along the way, we had misunderstood how microservices should be isolated and underestimated the importance of choosing the right boundaries between services. The only ways we could break down our monolith meant that implementing a standard ‘feature’ would involve updating multiple microservices at the same time. Having each feature requiring different combinations of microservices prevented any microservice from being owned by a single team.

The variation between microservices, instead of being a strength, became a disadvantage. Each feature would require learning how a new microservice worked and what changes other teams had made to it.

No real isolation

Because we couldn’t isolate any of our services properly, this was going to mean that we would be left with a significant amount of duplication. For example, we identified one particularly complicated and essential piece of business logic that would have to be copy-pasted and maintained across 4 of the planned microservices.

Yes IT is Domain-based

Ultimately, rather than separate our monolith into separate services, we started to break our solution into separate projects within the existing monolith. This division gave us a bit of additional structure and a better indication of where coupling and duplication existed, without the extra weight and challenges of microservices.

Further, this structure would make our domain models clearer, allowing us to evaluate candidates for any future microservices more easily.

====

The above is not unique.  When ‘leaders’ who read too many Gartner Group reports, or attend too many conferences and arbitrarily decree ‘Thou shalt implement Micro-Services on all projects’, problems, chaos and expensive failures ensue.

There needs to be a compelling reason to engage in the difficult task of decomposing a monolith.  For example, it took Amazon 2 full rewrites and about 7 years to create a Micro-Services architecture to support its business domain and scalability needs.  The rebuilding of its core application from a Java Monolith was a business necessity, since the load and increase in the number of users of its ecommerce platform could not be sustained otherwise.  But most firms are not Amazon and they do not scale from a few users at 6 am, to millions by 2 pm.  For many static business applications, a Monolith is fine.