The Microservices Debate: Are We Solving the Right Problems?

The Microservices Debate: Are We Solving the Right Problems?

Over the past decade, microservices architecture has been hailed as the ultimate solution to scaling modern applications. Yet, after interviewing CTOs and reflecting on my own experiences, I've begun to question whether microservices are the right approach for every problem.

In many cases, microservices add unnecessary complexity and don't address the real bottlenecks in business-critical systems. This article is a call to rethink the way we approach system architecture, particularly when it comes to scaling and ensuring data consistency.

The article is written with the needs of industrial manufacturing software in mind, so some of the arguments may not be valid in other domains. However, I believe that the concepts I describe have merit across all industries.


1. The Origins of Microservices: A Solution to Java’s Problems, Not Everyone’s

Many companies adopted microservices due to issues with Java’s compile- and deployment- times. These organizations struggled with monolithic Java applications where compiling a large JAR or WAR file became painfully slow. The microservices approach provided a way to decouple large systems into smaller, independently deployable services, reducing compile times.

However, as I learned from interviewing several CTOs, many of these problems are specific to Java monoliths. Other technologies don’t necessarily suffer from the same bottlenecks. Other languages like Go, Rust, or C++ are less bothered with these issues, and frameworks like Ruby (with Ruby on Rails), PHP (often used with Laravel), or Python (with Django) never had these issues to begin with. The problem with Java was extrapolated into a broader architectural shift that might not be necessary for other stacks.


2. The Pitfall of Chasing Smaller Services

One of the biggest misconceptions in microservices architecture is the idea that smaller services are always better. In reality, focusing on creating ever-smaller microservices introduces significant overhead in the form of serialization, deserialization, and network latency. This approach doesn't scale effectively and adds complexity to the system.

The real goal should be right-sized services, where the service size is determined by domain requirements and application needs, not arbitrary guidelines. Instead of aiming for minimalism, systems should be designed around business domains that keep functional units together.


3. Latency Can't Be Solved by Scaling Out

A common argument for microservices is that they allow for independent scaling. However, microservices do nothing to address latency—in fact, they often exacerbate it. When services communicate over a network, latency is inherent, and scaling out can't fix it.

Instead of working around latency by distributing services, it's far more effective to solve the root causes of latency. Optimizing the network infrastructure, database performance, and localizing critical processes are often more impactful than splitting services into smaller parts.


4. Microservices Often Ignore Multithreading

Many of the performance issues microservices aim to solve can be handled more efficiently with proper multithreading. In languages like Rust, C++, Java, or Go, multi-threaded applications can process multiple tasks concurrently without the need for distributed systems.

Microservices architecture often leads to separating logic into different processes, but this comes with overhead. For applications written in languages like Python or JavaScript, this might seem necessary because of poor multi-threading support. However, moving to languages with better concurrency models often proves to be a simpler and more efficient solution.


5. Multi-Engineer Development Doesn’t Require Microservices

One of the arguments for microservices is that multiple developers can work in parallel on different services. But with proper version control, modular libraries, and well-defined interfaces, this can also be achieved in a well-architected monolithic system.

Using tools like Git for branch management and continuous integration/continuous deployment (CI/CD) pipelines, teams can work simultaneously without needing to break down the application into microservices.

The independent deployment argument also falls apart when you consider the complexity of managing and orchestrating multiple services, which often outweighs the simplicity of deploying a single, well-architected system.


6. The Persistent Storage Bottleneck

The biggest challenge in scaling modern applications isn’t CPU or memory—it’s persistent storage. In business systems, ACID compliance (atomicity, consistency, isolation, durability) is crucial. Whether you’re dealing with financial transactions or inventory management, your system needs to ensure that data remains consistent.

Microservices may allow you to scale CPU resources, but they don’t solve the real bottleneck: database performance. When ACID compliance is required, your database often becomes the limiting factor. Trying to scale application services won't improve performance if the database can’t handle the load.

The solution isn’t to scale out microservices but to partition your system into ACID-compliant sub-systems or invest in better hardware (e.g., SSDs, RAID, multi-threaded architectures, and more memory). Scaling your database vertically, optimizing queries, and partitioning your data based on domains will yield better results than adding more microservices.


7. Multi-Modal Databases Are One of the Solutions

To manage both scalability and data integrity, the future lies in multi-modal databases like PostgreSQL and 甲骨文 , which can handle ACID transactions while also supporting various data types—relational, key-value, documents, and graphs.

These databases are capable of behaving like a traditional RDBMS while also providing the flexibility to store unstructured data as JSON documents, objects, graphs, vectors, or key-value pairs. This hybrid approach allows you to scale as needed without sacrificing consistency.

Contrary to popular belief, NoSQL and NewSQL databases are not a panacea. They often rely on eventual consistency, which might work for some applications but is inappropriate for mission-critical business systems that require strong guarantees of data integrity. In business, consistency is a decision, and it needs to be flexible depending on the use case—something multi-modal databases provide.


8. Independent Scaling vs. Efficient Hardware Utilization

While microservices promise independent scaling, the reality is that they often lead to inefficient hardware utilization. I’ve seen systems that achieved 40x to 100x performance improvements by consolidating microservices back into larger, well-architected systems.

Instead of distributing processes across multiple services (and introducing overhead), many businesses would be better off utilizing the full capabilities of modern hardware. With cloud providers like Amazon Web Services (AWS) offering EC2 instances with 896 vCPUs and 32TiB of memory, many applications don’t need more microservices—they need to take full advantage of existing infrastructure.


9. Avoiding the Term "Monolith"

The term “monolith” has become a pejorative, used to describe bloated and unmanageable systems. But in reality, many well-designed systems don’t fit the monolith vs. microservices binary.

I prefer the term “well-architected system”—a design that balances simplicity, scalability, and maintainability. A well-architected system can still have modularity, well-defined boundaries, and separation of concerns without needing to be broken down into dozens of independent services.


10. High-availability

Having many small services running in parallel is often cited as a good reason to use microservices. One of the arguments is that if any service fails, then it will have limited impact and that a new service often can be spun up. There are two aspects to this argument, one is the limited scope of each service that will only impact a small part of the system, second is the redundancy of multiple services doing the same thing.

I see the issue from different perspectives:

  1. Root cause: We first have to understand what it is that makes a service fail. Assuming that a service should have the logic to handle errors outside its scope, then it boils down to two underlying problems. Hardware issues and software (logic) issues. In my experience, it is often better to solve the hardware issues with better hardware. Server class systems offer redundant CPUs, error-correcting code (ECC) memory, chip-level fault tolerance, RAID (Redundant Array of Independent Disks) storage, and redundant networking. While it is true that many of the original big data and cloud architectures used low-cost hardware that often failed, modern data centers are built on solid server architectures. If we have a software issue, then that issue is likely present in all instances of the service, hence they will all eventually fail. We must fix the software to allow the system to run. I like using strictly typed systems such as Rust, C#, TypeScript, and Java instead of Python and JavaScript. I also prefer memory-safe systems to avoid segmentation faults.
  2. Culture: I like to contrast this with the Toyota Production System where the whole line stops if there is an issue on any part of the line. This approach ensures that the root cause of issues gets the right attention so that it can be fixed once and for all. If you have a culture that tolerates failure, then you will likely end up with a big mess. There is little tolerance for half-working solutions in manufacturing.


Final Thoughts: Focus on What Matters

In today’s architectural debates, it’s easy to get swept up by trends like microservices. But before adopting any architecture, we need to ask the fundamental question: What are we really trying to solve?

If the bottleneck is database performance, adding more services won’t help. If the goal is scalability, consider how much you can achieve by simply leveraging modern hardware and multi-threading. And if the priority is resilience, focus on simplifying your codebase and using better hardware to handle faults, rather than distributing complexity across services.

While I am challenging microservices, I am not saying that they are wrong for all applications. Ultimately, the best system is one that is well-architected for its specific needs. Whether that involves microservices, a single cohesive system, or something in between, the goal should always be to address the real bottlenecks and build for scalability, resilience, and simplicity.

As always, before you even start looking at the technical requirements, make sure that you understand, challenge, and simplify the business requirements. It may be that you are trying to solve a problem that does not exist. There is nothing more wasteful than an elegant solution to a problem that should not have been solved in the first place.


What are your thoughts about microservices and well-designed architectures? Do you agree or disagree with me? Is there anything that I have missed? Did you learn anything new?

I want to spur conversations, not sell a particular view, please feel free to say that I am wrong. The only thing I ask is that you provide context to your argument.


About the Author: I am a principal analyst and research director at LNS Research . While this article is not focused on the work I am doing for LNS, it touches on areas that often come up in our conversations with our members. In previous roles, I have spent years exploring both the advantages and challenges of modern software architectures. My focus has been on designing systems that balance efficiency, scalability, and simplicity, always with an eye on the real-world business needs driving technical decisions.

I love manufacturing and I enjoy tackling problems from both top-down and bottom-up. I have experiences that range from the boardroom to the factory floor.


How was this article created: This article represents my personal views. The article was written in collaboration with ChatGPT, but the content is based on my experiences and opinions, not necessarily those expressed by ChatGPT or the materials it was trained on.

The image was created by ChatGPT 4o and DALL-E, with the intent of creating something eye-catching, as always these images don't have much substance to them.


Tagging: #microservices, #softwarearchitecture, #php, #laravel, #python, #django, #java, #javascript, #rubyonrails, #ruby, #rustlanguage, #cplusplus, #golanguage, #postgres, #oracle, #database, #AWS, #Azure, #manufacturing


Edit log:

  1. I added Java to the list of languages with good multi-threading. My intent is not to pick on Java.
  2. I added the section on high-availability.

Divi L.

B2B Product Management and Marketing Leader | Transforming Manufacturing & Supply Chain through Innovation in SaaS, Cloud, AI and Analytics | Commitment to Operational Excellence | IIT Bombay | UT Austin

1 个月

Interesting perspective sure to elicit some healthy debate

回复
Ravi Abram

Product Management

2 个月

It would be good to differentiate “microservices” between a) the technical aspect of the term and its etymological bend to get to architectural integrity, app-team/ecosystem resilience, or containers vs. b) the service isolation achieved by the high availability of selective data, value-driven process management, and business simplification. A misplaced assumption by non-technical folk. And compound it with an industry focus - manufacturing in this case.

回复
Vatsal Shah

Founder & CEO Litmus

2 个月

You hit some of the key points on why to select and not to select. Obsession over everything microservice is bound to fail or get very expensive.? I believe there are two points of view on selecting microservices architecture as a product company. 1. Does it help users of our products? - Better scale at lower cost, improved security posture, increased resiliency, better extensions, or continuous improvements. 2. Does it help our engineers build the products? - Increase development velocity, better QA, better extensions, or lower development cost. From day 0, Litmus selected microservices inside the products but abstracted all the complexities from them for the users. Users don't know (or don't have to care) that it is microservices for about 90% of Litmus features. We see a spectrum of customer infrastructure, from ancient "mini PC"-based deployments to modern Azure ARC. We must comply with all and can't force deployment mechanisms over specific microservices. I will show you the Litmus architecture over a call—you will find it very interesting. It helped us achieve the largest Industrial DataOps deployments, whitelable six OEMs, 5-9s to 100% uptime, and unimaginable feature release velocity.

Prashant Jagarlapudi

Product & Engineering Leader

2 个月

I am glad you are talking about this Niels Erik Andersen. I believe that microservices became popular in the SaaS world where the hosting company controlled which versions of the microservice worked together - and there was only one such configuration. It helped in decoupling the development and deployment to reflect how the teams maybe collaborating. Many traditional enterprise software companies have tried to apply this approach, but find it impossible to manage the versions of each service and certify them for interoperability. Also, Microservices were viewed as a silver bullet to solve issues that were not rooted in decoupling - such as CPU utilization, data availability and ability to make changes to the functionality. Good article!!

Hugo Vaz

Just human | using ??,?,??,??,??,??,?? ; do{ bring_iiot_to_everyone(??); }while(alive); ?compiler : error ?? found in line 1. Fix yourself and re-compile live purpose app ?

2 个月

The microservices debate is fascinating, but for me, the ultimate goal of microservices architecture is to create a cohesive monolith. Think of a dinner table: the plate, knife, and fork are like microservices—each with a specific function. But together, they form a unified setting, a common understanding of how we dine. Sure, you could eat with your hands, but that's not considered proper etiquette, much like a system without proper abstraction and structure isn’t optimal. A microservices architecture can eventually become a monolith when there’s shared understanding in the environment or technology, leading to a well-integrated system. The struggle lies in how we often focus on ‘microservices versus monolith’ instead of how they can complement each other. It’s not about choosing one, but about leveraging microservices to evolve into a unified, seamless system when the right abstractions are in place.

要查看或添加评论,请登录

社区洞察

其他会员也浏览了