Essay · Computing
Microservices: When and Where to Start
Keywords
Overview
With the advent of cloud computing, software architecture concepts and approaches once considered “state of the art” became fundamental standards in software projects. That is because, beyond simply delivering applications as a service, what we used to treat as non-functional requirements became basic premises of software architecture. Characteristics such as elasticity, adaptability and portability are all part of this picture.

For a long time, client/server architectures were more than an architectural model. They were the “feasible standard” given what infrastructure made possible.
The purpose of this article (in two parts) is to examine microservices-based software architecture and how the roadmap for planning, designing and implementing microservices has taken shape.
Motivation: when do microservices make sense?
The first question people usually ask is: at what point do you realize that your monolithic application needs to be redesigned into a microservices-based architecture? Or, going further: in a greenfield scenario, where the software will be built from scratch, can I start out thinking of it as microservices?
From monoliths to microservices
Traditional three-tier architecture: you don’t build a castle on toothpick stilts

From university lecture halls to corporate offices, the three-tier development pattern has dominated — and, incidentally, still dominates — a good share of the world’s software repositories. Indeed, when we talk about design patterns, the cornerstone of software engineering is MVC (Model, View, Controller).
This pattern is so influential that virtually every development technology offers, in its main frameworks, an abstraction to streamline the implementation of this model — namely SpringMVC for Java, Django for Python, Rails for Ruby, among many others.

Naturally, there are many ways to implement this pattern. You can isolate the front end (presentation) with static calls to the back end (application and data); you can combine all three into a single block if there is no need for an external repository; or you can keep the three layers isolated but bundled into the same package to be run by a web server, for example. Viewed strictly from a development standpoint, it looks like the ideal model — especially considering that no project is born big: there are usually few people, few requirements and, above all, few resources to run the application. Everything starts to change when we ask, “what if we need to grow?”
What drives the motivation: size, coupling and elasticity
Applications start small
No application is born big. Lines of code don’t sprout from the ground — however stubbornly project managers may believe they do — and it takes time for a mere CRUD to turn into something about which one can say: yes, now we have an application!
Let’s start from the premise that the growth of a piece of software — in lines of code — is directly tied to two factors: a) its use — that is, the more it is used, the more “gee, it would be nice if it also did this or that” moments there will be; and b) a growing workload, at which point the sizing of the application’s infrastructure begins to be put to the test. In this second case the problem is even broader, since it means growing both horizontally — more requests and more traffic to your application call for more computing units — and vertically — making each of those computing units grow as well, so it can handle and process the work that has been added to its queue.
When one problem meets another and spawns an alien
As I mentioned earlier, as the cycle of adding new features to the application versus the growth in workload becomes more and more intense, the problems grow bigger and bigger too.
That is because new features in the application mean more lines of code, more testing, more integration points, more dependencies, and so on. On top of that comes running the application. The more traffic, the more executions, the greater the demand on infrastructure.
The natural strategy for meeting this rise in demand is to scale the application’s infrastructure horizontally and vertically. Even if this approach seems to solve the problem, let’s think at scale — that is, this scenario multiplied by 100, by 1,000, by 1,000,000… Shipping a simple fix to production becomes an event of global proportions.

It becomes clear that the problem with a monolithic architecture has more to do with its ability to scale rationally than with its nature as a design pattern per se. As Fowler (2017) wisely states, “scalability requires concurrency and partitioning: two conditions that are hard to achieve in a monolith.”
Another of the monolith’s infrastructure-level challenges is that scalability concerns an infrastructure’s ability to grow with demand. When we talk about born-in-the-cloud applications, however, this characteristic goes further, into what we call elasticity.
Whereas scalability makes it possible to add capacity as demand grows, elasticity makes it possible both to add and to remove capacity as demand dictates.
From design to construction: monoliths vs. microservices
Monolithic application architecture A monolithic application is generally described as software in which both the front end and the back end live together in a single software package. In other words, even if its source code is organized in layers (as in MVC), the final build is deployed as a single unit.
One of the main characteristics of monolithic applications concerns modularity. Generally speaking, this is an important characteristic when viewed from the perspective of software reuse, the maintainability of specific parts or structures, and allocating infrastructure to run individual routines rather than the whole software package, among other aspects.
In a monolith, all functions live inside a single application. Naturally, each function of a system has its own scope, resource demands and so forth. In a monolith, if you need to roll out an update, or even provision more resources so that it can process a given function faster, you will have to both redeploy the entire application — even to update a small part of it — and reprovision the infrastructure for that new scenario.
Microservices-based application architecture A microservice is nothing more than an application that usually performs just one task, but does so independently and more efficiently — since it has dedicated resources of its own. This “componentization” of the application enables a whole series of improvements in how applications are maintained, updated and scaled.
First, when a microservice needs to change, the process is simpler: a) because it is a specific application, changes become less complex to make; b) the maintainability of the application (the microservice) becomes simpler; c) if one function of the application needs more infrastructure, resources can be added specifically for that function.
In short, adopting a microservices-oriented architecture makes it possible to address aspects such as reducing technical debt, increasing developer productivity, shortening the learning curve, making testing more efficient and, finally, elasticity. A microservices architecture stands out by organizing an application as a set of small, independent services, each designed to serve one specific piece of the system’s functionality. These services communicate with one another through lightweight APIs, such as REST or event messages, promoting separation of concerns and isolation of functions. This approach makes for a more flexible architecture, in which each microservice can be developed, tested, deployed and scaled independently.
That independence is one of the great advantages of microservices. Compared with the monolithic approach, in which the entire application is a single block, a microservices architecture lets development teams work simultaneously on different parts of the system without affecting the application as a whole. The result is greater speed in delivering new features, bug fixes and performance improvements.
Microservices bring with them certain benefits that are essential in the context of cloud computing, such as elasticity and scalability. For example, if one feature of the application is in higher demand than others, the microservice behind it can be scaled horizontally (that is, replicated) to meet that specific demand without having to scale the entire application.
While a microservices architecture brings countless advantages in terms of modularity, it also introduces new challenges, chiefly in communication between services. Since each microservice is an independent component, communication among them must be carefully planned to preserve the cohesion and consistency of the system as a whole.
One important point is the communication protocol. Standards such as REST or gRPC are typically used, but the choice of technology depends on the nature of the project, its performance requirements and the interoperability it needs. It is worth considering that asynchronous, event-based communication (event-driven architecture) can be an efficient approach for integrating microservices, especially in scenarios where low latency and resilience are essential.
Then there is the question of transactions. In a monolithic system, complex transactions can easily be managed using persistence frameworks and relational databases. In a microservices architecture, however, since each service manages its own database and business logic, distributed transactions become a challenge. Approaches such as applying the “Sagas” pattern or using eventual consistency techniques are needed to ensure data integrity without compromising each microservice’s autonomy.

So when should I consider migrating from a monolith to microservices?
A common question for development teams and technology leaders is knowing when the time is right to migrate from a monolithic architecture to microservices. Highly beneficial as it is in many scenarios, this transition also carries costs and risks. First of all, a careful analysis of the existing monolithic application is essential. Questions such as “How hard is it to ship new features?”, “What are the barriers to scaling specific parts of the system?” and “Is there heavy interdependence between different parts of the application that makes maintenance difficult?” are signs that a migration is needed.
An incremental approach is usually the best choice, starting with the modules or features that cause the most trouble or are most critical to the business. A common technique is to identify a well-defined “bounded context” within the monolith that can be extracted as the first microservice. This gradual migration helps reduce risk and ensures that the benefits of microservices are achieved without major disruptions to the system’s operation.
In a greenfield project — that is, when a new system is being built from scratch — microservices can be considered from the outset, but with caution. Not every application needs the complexity of microservices; for smaller systems, a modular monolith may be a simpler and more effective approach. If high growth is expected, however, elasticity, development independence and the potential for scalability make a microservices architecture a strategic choice.
Challenges and best practices in implementing microservices
Despite its clear advantages, implementing a microservices-based architecture brings significant challenges that need to be met with sound engineering practices:
Observability and monitoring: Since microservices are independent and distributed, it is crucial to have a clear view of each service’s health. Observability tools that offer centralized logs, metrics and transaction tracing are essential to keep operations reliable.
Configuration management and service discovery: Microservices must be able to find one another at runtime, which can be complex in a dynamic environment such as the cloud. Service discovery tools, such as Consul or Kubernetes DNS, make this communication easier. Centralized configuration management is also vital to maintain consistency and reduce the risk of failures.
Version control and compatibility: As each microservice continuously evolves, it is important to have a clear strategy for versioning APIs and communication contracts. This prevents changes in one microservice from causing unexpected breakage in other services that depend on it.
Security and authorization: A microservices-based architecture calls for a different approach to security. Strategies such as centralized authentication via OAuth2 or OpenID Connect and service-level authorization policies are recommended practices for protecting inter-service communication and sensitive data.
Deployment automation and infrastructure as code: In a microservices environment, automation is essential to ensure that each service can be built, tested and deployed quickly and reliably. CI/CD tools (such as Jenkins, GitLab CI/CD or AWS CodePipeline) and infrastructure-as-code solutions (such as Terraform or CloudFormation) are indispensable for orchestrating the services’ lifecycle.
Microservices architecture presents itself as a flexible, scalable alternative for modern software development, especially in the context of cloud-native applications. Nevertheless, moving from a monolithic architecture to microservices requires careful planning and a thorough analysis of the needs of both the business and the application.
Adopting sound practices and the right tools is critical to successfully implementing and operating a microservices architecture, ensuring that it delivers all the expected benefits: modularity, scalability, resilience and development speed.
References:
[1] Fowler, Martin. Microsserviços prontos para produção. Novatec, 2017.
[2] Evans, Eric. Domain-Driven Design: Tackling Complexity in the Heart of Software. Addison-Wesley, 2003.
[3] Newman, Sam. Building Microservices: Designing Fine-Grained Systems. O'Reilly Media, 2015.
[4] Richardson, Chris. Microservices Patterns: With Examples in Java. Manning Publications, 2018.
[5] Fielding, Roy Thomas. Architectural Styles and the Design of Network-based Software Architectures. University of California, Irvine, 2000.
[6] Adzic, Gojko. Specification by Example: How Successful Teams Deliver the Right Software. Manning Publications, 2011.
[7] Cloud Native Computing Foundation. Cloud Native Landscape. Available at: https://landscape.cncf.io.
[8] ThoughtWorks. Evolutionary Architecture: Embracing Change. ThoughtWorks Inc., 2017.
[9] Bass, Len; Clements, Paul; Kazman, Rick. Software Architecture in Practice. Addison-Wesley, 2012.
[10] Kubernetes Documentation. "Service Discovery and Load Balancing". Available at: https://kubernetes.io/docs/concepts/services-networking/service/.
[11] AWS Documentation. "Designing Microservices on AWS". Available at:https://aws.amazon.com/architecture/microservices/.
Comments
Every comment is moderated before it appears here. Nothing is published automatically.
Loading…