Microservices

21 languages

- Article

- Talk

- Read

- Edit

- View history

Tools

From Wikipedia, the free encyclopedia

Some of this article's listed sources may not be reliable. Please help improve this article by looking for better, more reliable sources. Unreliable citations may be challenged and removed. (October 2018) (Learn how and when to remove this template message)

In software engineering, a microservice architecture is a variant of the service-oriented architecture structural style. It is an architectural pattern that arranges an application as a collection of loosely coupled, fine-grained services, communicating through lightweight protocols. One of its goals is that teams can develop and deploy their services independently of others. This is achieved by the reduction of several dependencies in the code base, allowing developers to evolve their services with limited restrictions from users, and for additional complexity to be hidden from users.[1] As a consequence, organizations are able to develop software with fast growth and size, as well as use off-the-shelf services more easily. Communication requirements are reduced. These benefits come at a cost to maintaining the decoupling. Interfaces need to be designed carefully and treated as a public API. One technique that is used is having multiple interfaces on the same service, or multiple versions of the same service, so as to not disrupt existing users of the code.

Introduction[edit]

There is no single definition for microservices. A consensus view has evolved over time in the industry. Some of the defining characteristics that are frequently cited include:

- Services in a microservice architecture are often processes that communicate over a network to fulfill a goal using technology-agnostic protocols such as HTTP.[2][3][4]

- Services are organized around business capabilities.[5]

- Services can be implemented using different programming languages, databases, hardware and software environments, depending on what fits best.[6]

- Services are small in size, messaging-enabled, bounded by contexts, autonomously developed, independently deployable,[7][6] decentralized and built and released with automated processes.[7]

A microservice is not a layer within a monolithic application (for example, the web controller or the backend-for-frontend).[8] Rather, it is a self-contained piece of business functionality with clear interfaces, and may, through its own internal components, implement a layered architecture. From a strategic perspective, microservice architecture essentially follows the Unix philosophy of "Do one thing and do it well".[9] Martin Fowler describes a microservices-based architecture as having the following properties:[2]

- Lends itself to a continuous delivery software development process.[10] A change to a small part of the application only requires rebuilding and redeploying only one or a small number of services.[11]

- Adheres to principles such as fine-grained interfaces (to independently deployable services), business-driven development (e.g. domain-driven design).[12]

It is common for microservices architectures to be adopted for cloud-native applications, serverless computing, and applications using lightweight container deployment. According to Fowler, because of the large number (when compared to monolithic application implementations) of services, decentralized continuous delivery and DevOps with holistic service monitoring are necessary to effectively develop, maintain, and operate such applications.[13] A consequence of (and rationale for) following this approach is that the individual microservices can be individually scaled. In the monolithic approach, an application supporting three functions would have to be scaled in its entirety even if only one of these functions had a resource constraint.[14] With microservices, only the microservice supporting the function with resource constraints needs to be scaled out, thus providing resource and cost optimization benefits.[15]

History[edit]

There are numerous claims as to the origin of the term microservices. As early as 2005, Peter Rodgers introduced the term "Micro-Web-Services" during a presentation at the Web Services Edge conference. Against conventional thinking and at the height of the SOAP service-oriented architecture (SOA) hype curve he argued for "REST-services" and on slide #4 of the conference presentation, he discusses "Software components are Micro-Web-Services".[16] He goes on to say "Micro-Services are composed using Unix-like pipelines (the Web meets Unix = true loose-coupling). Services can call services (+multiple language run-times). Complex service assemblies are abstracted behind simple URI interfaces. Any service, at any granularity, can be exposed." He described how a well-designed microservices platform "applies the underlying architectural principles of the Web and REST services together with Unix-like scheduling and pipelines to provide radical flexibility and improved simplicity in service-oriented architectures.[16]

Rodgers' work originated in 1999 with the Dexter research project at Hewlett Packard Labs, whose aim was to make code less brittle and to make large-scale, complex software systems robust to change.[17] Ultimately this path of research led to the development of resource-oriented computing (ROC), a generalized computation abstraction in which REST is a special subset.

In 2005 Alistair Cockburn wrote about hexagonal architecture which is a software design pattern that is used along with the microservices. This pattern makes the design of the microservice possible since it isolates in layers the business logic from the auxiliary services needed in order to deploy and run the microservice completely independent from others.

A workshop of software architects held near Venice in May 2011 used the term "microservice" to describe what the participants saw as a common architectural style that many of them had been recently exploring.[18] In May 2012, the same group decided on "microservices" as the most appropriate name. James Lewis presented some of those ideas as a case study in March 2012 at 33rd Degree in Kraków in Microservices - Java, the Unix Way,[19] as did Fred George[20] about the same time. Adrian Cockcroft, former director for the Cloud Systems at Netflix,[21] described this approach as "fine-grained SOA", pioneered the style at web-scale, as did many of the others mentioned in this article - Joe Walnes, Dan North, Evan Bottcher, and Graham Tackley.[22]

Microservices is a specialization of an implementation approach for service-oriented architectures used to build flexible, independently deployable software systems.[5] The microservices approach is the first realisation of SOA that followed the introduction of DevOps and is becoming more popular for building continuously deployed systems.[23]

In February 2020, the Cloud Microservices Market Research Report predicted that the global microservice architecture market size will increase at a CAGR of 21.37% from 2019 to 2026 and reach $3.1 billion by 2026.[24]

Service granularity[edit]

A key step in defining a microservice architecture is figuring out how big an individual microservice has to be. There is no consensus or litmus test for this, as the right answer depends on the business and organizational context.[25] For instance, Amazon uses a service-oriented architecture where service often maps 1:1 with a team of 3 to 10 engineers.[26] Generally, the terminology goes as such: services that are dedicated to a single task, such as calling a particular backend system or making a particular type of calculation, are called atomic services. Similarly, services that call such atomic services in order to consolidate an output, are called composite services.

It is considered bad practice to make the service too small, as then the runtime overhead and the operational complexity can overwhelm the benefits of the approach. When things get too fine-grained, alternative approaches must be considered - such as packaging the function as a library, moving the function into other microservices.[5]

If domain-driven design is being employed in modeling the domain for which the system is being built, then a microservice could be as small as an aggregate or as large as a bounded Context.[27]

In the granularity of microservices discussion, there is a spectrum, in one end there are the Anaemic Services, which do not have a large number of responsibilities, and on the other end, the Modular Monolith, which are large modules of a system.

Benefits[edit]

The benefit of decomposing an application into different smaller services are numerous:

- Modularity: This makes the application easier to understand, develop, test, and become more resilient to architecture erosion.[6] This benefit is often argued in comparison to the complexity of monolithic architectures.[28]

- Scalability: Since microservices are implemented and deployed independently of each other, i.e. they run within independent processes, they can be monitored and scaled independently.[29]

- Integration of heterogeneous and legacy systems: microservices is considered a viable means for modernizing existing monolithic software application.[30][31] There are experience reports of several companies who have successfully replaced (parts of) their existing software with microservices or are in the process of doing so.[32] The process for software modernization of legacy applications is done using an incremental approach.[33]

- Distributed development: it parallelizes development by enabling small autonomous teams to develop, deploy and scale their respective services independently.[34] It also allows the architecture of an individual service to emerge through continuous refactoring.[35] Microservice-based architectures facilitate continuous integration, continuous delivery and deployment.[36]

Criticism and concerns[edit]

The microservices approach is subject to criticism for a number of issues:

- Services form information barriers.[37]

- Inter-service calls over a network have a higher cost in terms of network latency and message processing time than in-process calls within a monolithic service process.[2]

- Testing and deployment are more complicated.[38][39]

- Moving responsibilities between services is more difficult.[6] It may involve communication between different teams, rewriting the functionality in another language or fitting it into a different infrastructure.[2] However, microservices can be deployed independently from the rest of the application, while teams working on monoliths need to synchronize to deploy together.[33]

- Viewing the size of services as the primary structuring mechanism can lead to too many services when the alternative of internal modularization may lead to a simpler design.[40] This requires understanding the overall architecture of the applications and interdependencies between components.[41]

- Two-phased commits are regarded as an anti-pattern in microservices-based architectures, resulting in a tighter coupling of all the participants within the transaction. However, the lack of this technology causes awkward dances which have to be implemented by all the transaction participants in order to maintain data consistency.[42]

- Development and support of many services are more challenging if they are built with different tools and technologies - this is especially a problem if engineers move between projects frequently.[43]

- The protocol typically used with microservices (HTTP) was designed for public-facing services, and as such is unsuitable for working internal microservices that often must be impeccably reliable.[44]

- While not specific to microservices, the decomposition methodology often uses functional decomposition, which does not handle changes in the requirements while still adding the complexity of services.[44]

- The very concept of microservice is misleading since there are only services. There is no sound definition of when a service starts or stops being a microservice.[44]

- Data aggregation. In order to have a full view of a working system, it is required to extract data sets from the microservices repositories and aggregate them into a single schema. For example, to be able to create operational reports that are not possible using a single microservice repository.

Cognitive load[edit]

The architecture introduces additional complexity and new problems to deal with, such as network latency, message format design,[45] backup/availability/consistency (BAC),[46] load balancing and fault tolerance.[39] All of these problems have to be addressed at scale. The complexity of a monolithic application does not disappear if it is re-implemented as a set of microservices. Some of the complexity gets translated into operational complexity.[47] Other places where the complexity manifests itself are increased network traffic and resulting in slower performance. Also, an application made up of any number of microservices has a larger number of interface points to access its respective ecosystem, which increases the architectural complexity.[48] Various organizing principles (such as hypermedia as the engine of application state (HATEOAS), interface and data model documentation captured via Swagger, etc.) have been applied to reduce the impact of such additional complexity.

Technologies[edit]

Computer microservices can be implemented in different programming languages and might use different infrastructures. Therefore, the most important technology choices are the way microservices communicate with each other (synchronous, asynchronous, UI integration) and the protocols used for the communication (RESTful HTTP, messaging, GraphQL ...). In a traditional system, most technology choices like the programming language impact the whole system. Therefore, the approach to choosing technologies is quite different.[49]

The Eclipse Foundation has published a specification for developing microservices, Eclipse MicroProfile.[50][51]

Service mesh[edit]

See also: Service mesh

In a service mesh, each service instance is paired with an instance of a reverse proxy server, called a service proxy, sidecar proxy, or sidecar. The service instance and sidecar proxy share a container, and the containers are managed by a container orchestration tool such as Kubernetes, Nomad, Docker Swarm, or DC/OS. The service proxies are responsible for communication with other service instances and can support capabilities such as service (instance) discovery, load balancing, authentication and authorization, secure communications, and others.

In a service mesh, the service instances and their sidecar proxies are said to make up the data plane, which includes not only data management but also request processing and response. The service mesh also includes a control plane for managing the interaction between services, mediated by their sidecar proxies.[citation needed]

A comparison of platforms[edit]

Implementing a microservice architecture is very difficult. There are many concerns (see table below) that any microservice architecture needs to address. Netflix developed a microservice framework to support their internal applications, and then open-sourced[52] many portions of that framework. Many of these tools have been popularized via the Spring Framework – they have been re-implemented as Spring-based tools under the umbrella of the Spring Cloud[53] project. The table below shows a comparison of an implementing feature from the Kubernetes ecosystem with an equivalent from the Spring Cloud world.[54] One noteworthy aspect of the Spring Cloud ecosystem is that they are all Java-based technologies, whereas Kubernetes is a polyglot runtime platform.

Microservices concern

Spring Cloud & Netflix OSS

Kubernetes

Configuration management:[55] configuration for a microservice application needs to be externalized from the code and be retrievable via a simple service call.

Spring Config Server, Netflix Archaius both support a Git-repository—based location for configuration. Archaius supports data typing of configuration.

Kubernetes ConfigMaps exposes the configuration stored in etcd via services. Kubernetes Secrets supports the service-based secure deployment and usage of sensitive configuration information (such as passwords, certificates, etc.).

Service discovery: maintain a list of service instances that are available for work within a microservice domain.

Spring Cloud Eureka allows clients to register to it, maintains a heartbeat with registered clients, and maps service names to hostnames for clients that lookup services by service name.

Kubernetes Services provide deployment-time registration of instances of services that are internally available within the cluster. Ingress is a mechanism whereby a service can be exposed to clients outside the cluster.

Load balancing: The key to scaling a distributed system is being able to run more than one instance of a component. Load has to be then distributed across those instances via a load balancer.

Spring Cloud Ribbon provides the ability for service clients to load balance across instances of the service.

Kubernetes Service provides the ability for the service to be load-balanced across service instances. This is not the equivalent of what Ribbon provides.

API gateway: The granularity of APIs provided by microservices is often different than what a service client needs. API Gateways implement facades and provide additional services like proxying, and protocol translation, and other management functions.

Spring Cloud Zuul provides configuration-based API facades

Kubernetes Service and Ingress resources, Istio, Ambassador are solutions that provide both north–south (traffic into and out of data center) as well as east–west (traffic across data centers or clouds or regions) API gateway functions. Zuul can also be implemented along with Kubernetes, providing configuration at individual service level.

Security concerns: Many security concerns are pushed to the API gateway implementation. With distributed microservice applications, it makes sense to not reinvent the security wheel and allow for policy definition and implementation in components that are shared by all services.

Spring Cloud Security addresses many security concerns through Spring Cloud Zuul

The Kubernetes ecosystem provides service meshes like Istio, which are capable of providing security through their API gateway mechanisms.

Centralized logging: It is important to have a centralized log gathering and analysis infrastructure to manage a plethora of services – many of which are operating in a distributed fashion.

ELK Stack (Elasticsearch, Logstash, Kibana)

EFK Stack (Elasticsearch, Fluentd, Kibana)

Centralized metrics: A centralized area where the health and performance of the individual services and overall system can be monitored is essential to proper operations.

Spring Spectator & Atlas

Heapster, Prometheus, & Grafana

Distributed tracing: Per-process logging and metric monitoring have their place, but neither can reconstruct the complex paths that transactions take as they propagate across a distributed system. Distributed tracing is an essential tool for a microservices platform.

Spring Cloud Sleuth

Hawkular, Jaeger

Resilience and fault tolerance: Distributed systems must be capable of auto-routing around failures, and be capable of routing requests to the service instance that will provide an optimum response.

Spring Hystrix, Turbine, & Ribbon

Health check, service meshes (example: Istio)[56]

Autoscaling and self-healing: Distributed systems respond to higher load by scaling horizontally: the platform must detect and auto-respond to such conditions. Furthermore, the system needs to detect failures and attempt auto-restarts without operator input.

-

Health check, self-healing, and auto-scaling

Packaging, deployment, and scheduling: Large-scale systems require robust package management, and deployment systems to manage rolling or blue-green deployments, and rollbacks if necessary. A scheduler helps determine which particular execution node a new set of services can be deployed to based on current conditions.

Spring Boot, Apache Maven. The Spring Cloud system does not have a true scheduler.

Docker, Rkt, Kubernetes Scheduler & Deployment, Helm[57]

Job management: scheduled computations disconnected from any individual user requests.

Spring Batch

Kubernetes Jobs and Scheduled Jobs

Singleton application: limit a specific service to run as the only instance of that service within the entire system.

Spring Cloud Cluster

Kubernetes Pods

See also[edit]

- Conway's law

- Cross-cutting concern

- Data mesh, a domain-oriented data architecture

- DevOps

- Fallacies of distributed computing

- GraphQL

- gRPC

- Representational state transfer (REST)

- Service-oriented architecture (SOA)

- Microfrontend

- Unix philosophy

- Self-contained system (software)

- Serverless computing

- Web-oriented architecture (WOA)

Microservices - Not A Free Lunch!

TUESDAY, APRIL 8, 2014 AT 8:54AM

This is a guest post by Benjamin Wootton, CTO of Contino, a London based consultancy specialising in applying DevOps and Continuous Delivery to software delivery projects.

Microservices are a style of software architecture that involves delivering systems as a set of very small, granular, independent collaborating services.

Though they aren't a particularly new idea, Microservices seem to have exploded in popularity this year, with articles, conference tracks, and Twitter streams waxing lyrical about the benefits of building software systems in this style.

This popularity is partly off the back of trends such as Cloud, DevOps and Continuous Delivery coming together as enablers for this kind of approach, and partly off the back of great work at companies such as Netflix who have very visibly applied the pattern to great effect.

Let me say up front that I am a fan of the approach. Microservices architectures have lots of very real and significant benefits:

- The services themselves are very simple, focussing on doing one thing well;

- Each service can be built using the best and most appropriate tool for the job;

- Systems built in this way are inherently loosely coupled;

- Multiple developers and teams can deliver relatively independently of each other under this model;

- They are a great enabler for continuous delivery, allowing frequent releases whilst keeping the rest of the system available and stable.

This said, Microservices are not a free lunch!

I am currently involved in architecting a system based around Microservices, and whilst the individual services are very simple, a lot of complexity exists at a higher level level in terms of managing these services and orchestrating business processes throughout them.

Microservices one of these ideas that are nice in practice, but all manner of complexity comes out when it meets reality. For this reason, I wanted to write this article to capture some of these and redress the balance.

Significant Operations Overhead

A Microservices architecture brings a lot of operations overhead.

Where a monolithic application might have been deployed to a small application server cluster, you now have tens of separate services to build, test, deploy and run, potentially in polyglot languages and environments.

All of these services potentially need clustering for failover and resilience, turning your single monolithic system into, say, 20 services consisting of 40-60 processes after we've added resilience.

Throw in load balancers and messaging layers for plumbing between the services and the estate starts to become pretty large when compared to that single monolithic application that delivered the equivalent business functionality!

Productionising all of this needs high quality monitoring and operations infrastructure. Keeping an application server running can be a full time job, but we now have to ensure that tens or even hundreds of processes stay up, don't run out of disk space, don't deadlock, stay performant. It's a daunting task.

Physically shipping this plethora of Microservices through your pipeline and into production also needs a very high degree of robust and release and deployment automation.

Currently, there is not much in terms of frameworks and open source tooling to support this from an operational perspective. It's likely therefore that a team rolling out Microservices will need to make significant investment in custom scripting or development to manage these processes before they write a line of code that delivers business value.

Operations is the most obvious and commonly held objection towards the model, though it is too easily brushed aside by proponents of this architecture.

Substantial DevOps Skills Required

Where a development team might have been able to bring up, say, a Tomcat cluster and keep it available, the operations challenges of keeping Microservices up and available mean you definitely need high quality DevOps and release automation skills embedded within your development team.

You simply can't throw applications built in this style over the wall to an operations team. The development team need to be very operationally focussed and production aware, as a Microservices based application is very tightly integrated into it's environmental context.

Idiomatic use of this architecture also means that many of the services will also need their own data stores. Of course, these could also be polyglot (the right tool for the job!), which means that the venerable DBA now needs to be replaced with developers who have a good understanding of how to deploy, run, optimise and support a handful of NoSQL products.

Developers with a strong DevOps profile like this are hard to find, so your hiring challenge just became an order of magnitude more difficult if you go down this path.

Implicit Interfaces

As soon as you break a system into collaborating components, you are introducing interfaces between them. Interfaces act as contracts, with both sides needing to exchange the same message formats and having the same semantic understand of those messages.

Change syntax or semantics on one side of the contract and all other services need to understand that change. In a Microservices environment, this might mean that simple cross cutting changes end up requiring changes to many different components, all needing to be released in co-ordinated ways.

Sure, we can avoid some of these changes with backwards compatibility approaches, but you often find that a business driven requirements prohibit staged releases anyway. Releasing a new product line or an externally mandated regulatory change for instance can force our hand to release lots of services together. This represents additional release risk over the alternative monolithic application due to the integration points.

If we let collaborating services move ahead and become out of sync, perhaps in a canary releasing style, the effects of changing message formats can become very hard to visualise.

Again, bckwards compatibility is not a panacea here to the degree that Microservices evangelists claim.

Duplication Of Effort

Imagine that there is a new business requirement to calculate tax differently for a certain product line. We have a few choices in how to deliver this.

We could introduce a new service and allow the other services to call into this where needed. That does however introduce more potentially synchronous coupling into the system, so is not a decision we would take lightly.

We could duplicate the effort, adding the tax calculation into all of the services that need it. Besides the duplicated development effort, repeating ourselves in this way is generally considered a bad idea as every instance of the code will need to be tested and maintained going forward.

The final option is to share resources such as a tax calculating library between the services. This can be useful, but it won't always work in a polyglot environment and introduces coupling which may mean that services have to be released in parallel to maintain the implicit interface between them. This coupling essentially mitigates a lot of the benefits of Microservices approaches.

It seems to me that all three of these options are sub-optimal as opposed to writing the piece of code once and making it available throughout the monolithic application. The teams I have seen working in this style tend towards option 2, duplicating of business logic, which goes against many principles of good software engineering. And yes, this even takes place in well decomposed and designed systems - it's not always a sign of bad service boundaries.

Distributed System Complexity

Microservices imply a distributed system. Where before we might have had a method call acting as a subsystem boundary, we now introduce lots of remote procedure calls, REST APIs or messaging to glue components together across different processes and servers.

Once we have distributed a system, we have to consider a whole host of concerns that we didn't before. Network latency, fault tolerance, message serialisation, unreliable networks, asynchronicity, versioning, varying loads within our application tiers etc.

Coding for some of these is a good thing. Backwards compatibility and graceful degradation are nice properties to have that we might not have implemented within the monolithic alternative, helping keep the system up and more highly available than the monolithic application would be.

The cost of this however is that the application developer has to think about all of these things that they didn't have to before. Distributed systems are an order of magnitude more difficult to develop and test against, so again the bar is raised vs building that unsexy monolithic application.

Asynchronicity Is Difficult!

Related to the abovei point, systems built in the Microservices style are likely to be much more asynchronous than monolithic applications, leaning on messaging and parallelism to deliver their functionality.

Asynchronous systems are great when we can decompose work into genuinely separate independent tasks which can happen out of order at different times.

However, when things have to happen synchronously or transactionally in an inherently Asynchronous architecture, things get complex with us needing to manage correlation IDs and distributed transactions to tie various actions together.

Testability Challenges

With so many services all evolving at different paces and different services rolling out canary releases internally, it can be difficult to recreate environments in a consistent way for either manual or automated testing. When we add in asynchronicity and dynamic message loads, it becomes much harder to test systems built in this style and gain confidence in the set of services that we are about to release into production.

We can test the individual service, but in this dynamic environment, very subtle behaviours can emerge from the interactions of the services which are hard to visualise and speculate on, let alone comprehensively test for.

Idiomatic Microservices involves placing less emphasis on testing and more on monitoring so we can spot anomalies in production and quickly roll back or take appropriate action. I am a big believer in this approach - lowing the barriers to release and leaning continuous delivery in order to speed up lean delivery. However, as someone who has also spent years applying test automation to gain confidence prior to release, anything that reduces this capability feels like a high price to pay, especially in risk averse regulated environments where bugs can have significant repercussions.

To Conclude

These are some of the difficulties I am seeing during the early phases of building and running a Microservices based system.

I am still a fan of the approach and believe that on the right project with the right team it is a wonderful architecture to adopt where the benefits outweigh the costs above.

However, when considering Microservice like architectures, it's really important to not be attracted to the hype on this one as the challenges and costs are as real as the benefits.

Related Articles

- On Hacker News

AuthorHighScalability Team |

Comment23 Comments |

PermalinkPermalink |

Share ArticleShare Article

Print ArticlePrint Article

Email ArticleEmail Article

Tweet

Reader Comments (23)

I am moving to a microservices architecture because I'm moving beyond things that are affordable to do with a monolithic architecture. A big issue is that I find there are a very queries that have high latency and to get the latency down with a monolithic system means expensive overprovisioning or alternately, carefully teasing the system apart to optimize the data structures for this query without breaking any part of the system.

Here are my take on the challenges.

Even though people will celebrate the benefits of decoupling here, in the sense that one server could be written in LISP and another in COBOL and another in FORTRAN, this is only a low-cost, high-quality answer if there is a lot of standardization. For instance, if you are working in Java, you may plan to deploy all of the microservers as Tomcat applications or even be more specific and standardize on using Jetty.

You need to have out-of-the-box answers for all of the cumbersome things as to how you package microservers for deployment, how serialization and deserialization work, how you configure database connections and things like that. You really need a template of some sort so a developer can create a new "Hello World" service that is immediately deployable into production in 5 minutes. If it isn't that easy, people are going to find it easier to do things in random ways and then life will be hell for devops.

A deeper architectural problems is minimizing coupling, not in a cargo-cult way, but in a real way. One problem I have seen with SOA apps is often a change to an app such as adding a new field to the data model requires changes to a large number of different servers. One person might be tasked with this "simple" change, but making it involves tracking down everything affected by the change and understanding a number of code areas that were written by different people, probably deploying something that is only 95% to production and then the stressful problem of fixing the last 5% in a hurry.

I was talking with a Ruby on Rails guy and he was like "What's the problem? Can't you just add a field?" and the point is that you can in RoR because it is architected that way, and a microserver architecture should be also. There should be centralization of serialization-deserialization, for instance, so you can make a change in one place and not have to do a lot of tedious and error-prone stuff.

Connected with that is the issue of data updates and data integrity across the system. If you tried to put multiple microservers inside a distributed transaction boundary I think you would lose the benefits of microservers. Without transactions I think it's inevitable that minor data corruption will occur in the system. It's got to be built to sweep those problems under the rug and to clean up problems over time. (You can't ever throw a 500 error because data you wanted was missing) Connected with that is some systematic approach to updating. If you change one data item, maybe that means you update 20 microservers. Unless you have some strategy for updating that is provably correct, I think it's almost impossible that you'll get it right, particularly in a system that is always changing to meet new requirements.

April 9, 2014 |

Unregistered CommenterPaul A Houle

Being one of the tech leads on transforming a monolithic Java application to a SOA implementation, I've come across everyone of the issues you raise but instead of seeing those as problems I see them as opportunities to build software better.

First of all, as an aside, no one can clearly explain the difference between a micro service and a service. My service might be your micro service and your service might be my micro service. They are both services and should adhere to the same engineering principals. At our company we go with a service definition that says a service is a "business capability".

You say "Substantial DevOps Skills Required". I see that as a good thing. It gives the people writing the code the responsibility of how it runs in production. Going to a SOA implementation almost forces you to a de-centralized devops where the service team devs do the devops as compared to the old school "throwing it over the wall" to the centralized operations team. It's a big positive to have the dev team be responsible for the operations of their code.

I'd like to address your issue of increased operational complexity. Yes, there are more services compared to a monolithic application to build, test, and deploy but in today's world those things should all be automated. Having two vs. twenty that follow the same automated patterns should not be that much more work.

I agree with many of your points on "Implicit Interfaces" but I would point out that most of your points apply to any API whether in a service or a monolithic application. It's never a good thing to change the shape of an existing interface. That will end up bad. Most companies tend to create a new API version for interface shape changes. With a good API design, you want those to not happen very often and you should design your service and supporting systems in a way that you can still do daily code commits into production without changing the interface contract.

You mention that duplication of code goes against many good principals of software engineering. I used to be a purest in this area in that any and all code duplication is bad but then realized this purity sometimes resulted in much more costly and complex solutions and no one won but idealism. I'm now more practical in this area and simplicity has to be a part of the equation as well. I really like what Richard Gabriel wrote in The Rise of Worse is Better about this.

Testing in a SOA world can be complex due to other service dependencies but thinking about how you are going to test your service up front may end up saving you lots of time in the end. You will not have a good time if you design a service that has many other services that you require to be up and running while you test your service.

Asynchronous programming can be difficult but it doesn't have to be. Things like functional reactive programming and different async libraries like the Clojure core.async and Node.js async help developers be more productive in writing async code. But as a programmer, whether or not you use services, bets are you need asynchronous behavior somewhere in your application. So I don't see this issue as unique to services.

Thanks for addressing the subject of micro services since many of us are eager to learn more about this and can share our experiences.

April 10, 2014 |

Unregistered CommenterSteve Willcox

I recently wrote about this topic at peopleandcode.blogspot.in. It is not a free lunch, but you always need to know the trade offs. The decision to partition the system into parts (services or otherwise) needs Agility at the heart of the organization.

April 11, 2014 |

Registered Commenterunmesh joshi

Hi Ben

Thanks for a great insight into the realities of building a microservice architecture from the ground up. I'm a big fan of the microservices approach - particularly for Continuous Delivery - but there is definitely a sweet spot in terms of service composition.

I think of a microservice as an endpoint hosting a single business capability, whereas a lot of people go further and think of an endpoint hosting a single infrastructure capability. I want a service to have a single reason to change - a business reason to change - with minimal dependencies and minimal impact upon the rest of the estate. If this is accomplished then enormous benefits can be delivered. Once you drill down to infrastructure-centric microservices, you will face a lot of trade-offs in terms of operations/monitoring investment, distributed transaction management, etc.

Your point about microservices operability being dependent upon DevOps is a good one. I've certainly see monoliths broken up into microapplications or microservices by development teams, with the net result being an unhappy operations team swamped with new services to monitor and manage.

A final point - there is a cheap solution to your concern about Implicit Interfaces, and the answer is Consumer Driven Contracts (http://martinfowler.com/articles/consumerDrivenContracts.html and http://www.alwaysagileconsulting.com/application-pattern-consumer-driven-contracts/). Consumer Driven Contracts enable interfaces to rapidly evolve with minimal impact upon consumers, and that is crucial in a microservices architecture.

Cheers

Steve

April 12, 2014 |

Unregistered CommenterSteve Smith

For anyone reading this in London we have organised an event around the subject of Micro Services at the Stack Exchange offices in Old Street. Rackspace, Zeebox and British Gas connected homes are doing presentations on how they are organising their development teams around many small services.

The address for the meetup is http://www.meetup.com/DevOps-Exchange-London/events/169675532/

April 13, 2014 |

Unregistered CommenterSteven Acreman

I'm one of those enthusiasts about micro-services architecture. I agree with you regarding almost all mentioned points but I believe this architecture as many others shouldn't be taken as a whole if you don't have all resources needed for that.

However, even if your team members don't have DevOps knowledge to deploy and monitoring micro-services you can use the concept of the architecture. You can develop your services as if they would run distributed but end up packing them together into a big monolithic application, until someone from your team manage to automate things, making the deployment of each micro-service easy for all team members.

Take it slowly, get your team familiar with it, I'm doing it now, I hope it works. As soon as I get some results I will add more comments.

April 13, 2014 |

Unregistered CommenterEduardo Nunes

Nice post. I was working on a project moving towards micro-services from a monolithic application and we ran into a lot of the hurdles mentioned in your post. We ended up having a lot of code duplication (since the services were built on different languages and frameworks) - lots of little "implicit contracts", for example mapping User data from one service to another (one service not necessarily needing all the same data as another). While there are some clear benefits of the approach, probably not something you want to jump into without some careful planning. Our approach in the end was to modularize the monolithic application (so we can share code repository, deployments, and code between modules - but still have nice, loosely coupled components) and pull out the modules into their own independent micro-services that can be deployed/managed independently only if really necessary.

April 13, 2014 |

Unregistered CommenterBrady

Of course, we all know there is no such thing as a free lunch. Trade-offs are unavoidable in ours and most other enginerering disciplines. It all depends on the problem you are trying to solve. Sometimes a monolithic style may work best. I've been researching the topic for over a year after over 12 years of being frustrated with J2/Java EE apps. My thoughts are shared at microsvcs.io.

April 15, 2014 |

Unregistered CommenterJason Chambers

Quality post and commentary. I'm only just discovering SOA as I bounce between refining requirements and architectural planning for a project of mine. It's really small compared to the projects I usually see referenced in discussions like this, but it will definitely benefit from such informed criticism. Please continue to share!

April 17, 2014 |

Unregistered CommenterKirby

Great post Ben - we're currently implementing CD pipeline automation framework for a client that has over 450 developers working across 50 services (or microservices). To me one the most fascinating aspects of this architecture is none of those 450 developers will ever write a single line of code to support a customer facing user interface. All customer facing UX work is performed by a different group entirely.

While the level overall flexibility, risk reduction and cost savings this client currently enjoys is significant and a direct result of moving away from a monolithic architecture, there is no doubt a very real "microservice tax" paid due to many of the factors you very articulately outline in your post!

-

April 17, 2014 |

Unregistered CommenterDennis Ehle

Sounds like the same old, same old, issues the software industry has been grappling with since I came aboard in the late 80s. All that's happened is that the issues have been relabeled, renamed, and kicked up a few layers / degrees of abstraction. Your microservices are equivalent to 80s subroutines and functions. The issues with interfaces and the propagation of changes HERE to changes THERE and EVERYWHERE, are exactly the same as we faced "back then" in trying to change a function signature. Your multi-disciplinary team is the 80s' one-man-one-vision programmer who knew all the languages and the entire architecture of an entire system -- or maybe that's your hard-to-find-and-hire DevOps-knowledgeable developer (I'm a little handicapped by not actually knowing what "DevOps" IS; but I'm sure if I did, the 80s analog for that, too, would be easy to spot). We "solved" these 80s problems by developing Object-Oriented technologies -- so, clearly (to me), what the world needs now is the higher-level-of-abstraction analogue of OO. Call it whatever you want; what you're looking for is a hyper-object that somehow encapsulates all the little microservice contracts/APIs into an abstraction that hides all that. LEARN FROM THE PAST, dudes.

April 23, 2014 |

Unregistered CommenterChris Chiesa

As the author of SwarmESB, I agree with many points that the author and others in comments made about micro-services but I want to tell you about a new programming model ( I call swarming) that is related with older academic experiments (mobile code, an inverted perspectives of actors,etc).

For SwarmESB I use Node.js, because node is good for implementing the swarming concept but enterprise people (eg Java ) could easily use SwarmESB as an integration point for services exposing REST APIs. The main point is that service orchestration can be programmed in java script plus an abstract model of moving execution in nodes as required (swarm scripts execution). Transactions, monitoring and other aspects discussed here can be part of the swarm level of the ESB.

April 25, 2014 |

Unregistered CommenterAlboaie Sînică

Great post. I'm working at an organisation that has faced all the challenges you mention in their microservices implementation. And like some of your other readers have mentioned, it has provided opportunities for devops and design skill development, which has greatly has improved the culture and capabilities of the organisation.

The impact of several of the problems you mentioned (integration testing, setting up test environments, the inability to easily visualise the impact of changes, versioning and backwards compatibility) has been mitigated by our use of consumer driven contracts, specifically the use of the open source "pact" library, which I have had the pleasure of contributing to. It gives us the ability to run standalone CI builds, without extra environments, and know immediately if any change to our services will break anything. The ability to mine the relationship data to create visualisations and autogenerated documentation is also particularly useful, as conceptualising the interactions of 50+ microservices can become an overhead in itself.

May 22, 2014 |

Unregistered CommenterBethesque

The concept of miroservices is not really new, the term is. Good software engineers think/design modular code. I have been in polygot environments with "microservices" deployed that had all the above challenges you speak of. Netflix makes a big deal of microservices but most people skim over the fact that they have a homogenous java based environment for the most part.

I wanted to see how other people were tackling the challenge while still staying true to SOA ie. how do I keep my service contract clean/consistent and yet build small microservice functionality, how do I inculcate the DevOps mentality and meet business demands in an agile fashion. Through my (re)search, I came across this github project 'lightblue' - https://github.com/lightblue-platform/lightblue/wiki. These guys are putting a different spin on the microservice concept, building pluggable modules that I can use for backend interaction (SQL, NoSQL, libraries) while keeping the contract for applications to call the service consistent and search based. With the world moving towards big data, what better way to ensure your APIs meet changing demands than building search based APIs. Standardizing on the CRUD layer and then building modules seems to me a very intuitive way to approach the problem, and I get to decide if I want a monolithic layer or a microservice layer.I know I'm going to keep a watch on project lightblue.

June 20, 2014 |

Unregistered Commenterhitchhiker

About this:

"We "solved" these 80s problems by developing Object-Oriented technologies -- so, clearly (to me), what the world needs now is the higher-level-of-abstraction analogue of OO. Call it whatever you want; what you're looking for is a hyper-object that somehow encapsulates all the little microservice contracts/APIs into an abstraction that hides all that."

I would put the metaphor somewhat differently. Micro-services can be compared to objects. But then the question comes up, what is the equivalent of dependency injection for micro-services? The difficulty of wiring objects together was solved with dependency injection. What is the metaphorical equivalent for wiring together the micro-services?

June 29, 2014 |

Unregistered CommenterLawrence

The glue has to be a standard language or "protocol" - and it has to be designed to accommodate failure. That's the fun part about designing languages, in general. :)

I have limited tech experience, so do pardon the abstraction of my comment.

July 27, 2014 |

Unregistered CommenterJerng

Hi,

we started to divide our monolith into some business oriented services. A service comes around a business unit - so it is not micro but comprehensible.We will have a kind of structure for communication, that a service can poll data but did not push directly, and have no dependencies between the services. So far theory.

But we have always again discussions, where to see the boundaries or home for some specific data. Is there a recommendation, how to deal with data and belonging operations that have dependencies to two or more services. (formerly there were tables with several foreign keys that points to other parts of the application).

Thanks

December 3, 2014 |

Unregistered Commenterahs

Great post and valid points on the hurdles to making microservices a reality when moving from a monolith or even starting from scratch. Like some above commenters said developing and thinking in microservices is not necessary something new, but I think we now finally have a word for it and thus are able to communicate it better and discuss about what a true microservice architecture should look like. I'm seeing more and more discussion on fat vs. thin services, inclusion vs exclusion of DBs, "SOA = microservice or only a predecessor" and many other parts of the microservice topic being discussed nowadays. We are also seeing an increase in Google search volume, so more and more people are actually interested in this.

There are not only new problems arising with this, but also a lot of new tools and such that need to be learned and deployed. We also see some other trends (or hypes) getting combined into the microservice trend. For example the revival of container technologies, especially with Docker getting more and more widely known and stable. And it's easy enough for people to actually use it (at least for dev and testing). I think Docker and similar (or future) container technologies can help with at least some of the problems mentioned (especially when it comes to decoupling dev and ops dependencies). However, many of the mentioned problems still remain, especially when it goes into production and deployment. Handling your operations yourself is already quite some work without microservices, but with them as you also mentioned, the work just increases manyfold. Suddenly you have to take care of several servers, environments, message queues, load balancers, clustering,...We ran into the same problems and we tried a lot of stuff. Sure Netflix, Twitter, Spotify, Shopify and the likes can pull this feat off. But even those have a lot of self-developed stuff and "hacks" running in between to make it bearable for dev and ops alike. Thus, we thought maybe we could build something for the rest of us and got a running alpha now on Giant Swarm. It's a simple infrastructure, where you describe your microservice architecture (based on Docker containers) in a simple JSON file and we take care of the rest, incl. clustering, scaling, load balancing and such. With time we will also add more specific microservice "enablers" like a ready to use message queue for example. Seems like a lot of you here are interested in or already working with microservices, so we would be happy to let you try out our system and tell us what you need from a perfect microservice infrastructure, so we can build it for you.

December 18, 2014 |

Unregistered CommenterPuja Abbassi

This all micro services idea has roots in UNIX commands which than have to use shell to have meaningful application.

Another words access to micro services has to be simplified so another layer has to be in place. Is it performance hit ?

December 22, 2014 |

Unregistered CommenterJohn Smith

First, very interesting post.

As someone says, nothing really new with microservice. We had similar problems and questions with corba. Why ? Because the problem is not technology but is about business and urbanism.

The most important is not really the size of the application but the duplication or not of responsibilities. In an IS the most important is to clearly define who is responsible to do what. And this must be organized around domain models and use cases. We often forget the domain model in SOA like approaches. And this is the difficulty, what are the rights, just sub-context that allow me to define the good sized application; with enough responsibility and not to much dependencies with other applications ?

So the problem is more about urbanism and domain model sharing than the size of your application or service

December 24, 2014 |

Unregistered Commentererik gollot

Hello Ben,

Thank you for the great article - a very realistic view of micro services.

The only part where I disagree with you is on Duplication of Effort. There is one more option that you did not propose: extend the format of the message and add a strategy to handle the specific tax calculation for the corresponding product line. This approach limits changes to adding a strategy to your Tax Calculation Service, a new field to the reuqest's JSON payload, and a line or two of code to the calling service that populates the message. If a client (service) does not need that type of calculation, it can omit it. The Tax Calculation Service remains fully backward compatible, so there will be no need to go to option 1, as you suggested in your post.

Thanks again for sharing your experience with us. Your article opened my eyes to a lot of problems with micro services that I previously glossed over.

January 27, 2016 |

Unregistered CommenterGeorge Smith

There is alternative for REST/JSON services, Enduro/X middleware, which allows to write high throughput microservices. The speed is gained by fact that for inter process communications operating systems kernel queues/shared memory is used, i.e. no use of TCP on local machine, by communicating two or more processes. Also Enduro/X supports STRING, JSON and own binary protocol buffer - UBF format, which contains typed fields with low cost parsing times.

June 28, 2016 |

Unregistered CommenterMadars Vitolins

Although this article contains interesting insights, I would have felt more inclined to give it a 5 stars if it had not tried to pit microservices against monolith applications and completely ignored SOA. Comparing microservices with SOA would have been a lot more enlightening for many of us. Since 90% of the issues described actually correspond to services, as in SOA, one could actually reflect on what microservices bring to the IT and business applications world that web services, REST services or API do not already offer within SOA.

I can´t help feeling that comparing microservices with monolithic apps should no even be a talking point any more. Does anybody doubt, as to this date, that services - under whatever form- are the next and only possible step above monolith applications?

So, yes, lots of insight in this article about services. What about dropping the "micro"? :)

Microservice Trade-Offs

Many development teams have found the microservices architectural style to be a superior approach to a monolithic architecture. But other teams have found them to be a productivity-sapping burden. Like any architectural style, microservices bring costs and benefits. To make a sensible choice you have to understand these and apply them to your specific context.

Photo of Martin Fowler

Martin Fowler

01 July 2015

MICROSERVICES

Microservices provide benefits…

- Strong Module Boundaries: Microservices reinforce modular structure, which is particularly important for larger teams.

- Independent Deployment: Simple services are easier to deploy, and since they are autonomous, are less likely to cause system failures when they go wrong.

- Technology Diversity: With microservices you can mix multiple languages, development frameworks and data-storage technologies.

…but come with costs

- Distribution: Distributed systems are harder to program, since remote calls are slow and are always at risk of failure.

- Eventual Consistency: Maintaining strong consistency is extremely difficult for a distributed system, which means everyone has to manage eventual consistency.

- Operational Complexity: You need a mature operations team to manage lots of services, which are being redeployed regularly.

Summing Up

Strong Module Boundaries (Pro)

The first big benefit of microservices is strong module boundaries. This is an important benefit yet a strange one, because there is no reason, in theory, why a microservices should have stronger module boundaries than a monolith.

So what do I mean by a strong module boundary? I think most people would agree that it's good to divide up software into modules: chunks of software that are decoupled from each other. You want your modules to work so that if I need to change part of a system, most of the time I only need to understand a small part of that system to make the change, and I can find that small part pretty easily. Good modular structure is useful in any program, but becomes exponentially more important as the software grows in size. Perhaps more importantly, it grows more in importance as the team developing it grows in size.

Advocates of microservices are quick to introduce Conway's Law, the notion that the structure of a software system mirrors the communication structure of the organization that built it. With larger teams, particularly if these teams are based in different locations, it's important to structure the software to recognize that inter-team communications will be less frequent and more formal than those within a team. Microservices allow each team to look after relatively independent units with that kind of communication pattern.

As I said earlier, there's no reason why a monolithic system shouldn't have a good modular structure. [1] But many people have observed that it seems rare, hence the Big Ball of Mud is most common architectural pattern. Indeed this frustration with the common fate of monoliths is what's driven several teams to microservices. The decoupling with modules works because the module boundaries are a barrier to references between modules. The trouble is that, with a monolithic system, it's usually pretty easy to sneak around the barrier. Doing this can be a useful tactical shortcut to getting features built quickly, but done widely they undermine the modular structure and trash the team's productivity. Putting the modules into separate services makes the boundaries firmer, making it much harder to find these cancerous workarounds.

An important aspect of this coupling is persistent data. One of the key characteristics of microservices is Decentralized Data Management, which says that each service manages its own database and any other service must go through the service's API to get at it. This eliminates Integration Databases, which are a major source of nasty coupling in larger systems.

It's important to stress that it's perfectly possible to have firm module boundaries with a monolith, but it requires discipline. Similarly you can get a Big Ball of Microservice Mud, but it requires more effort to do the wrong thing. The way I look at, using microservices increases the probability that you'll get better modularity. If you're confident in your team's discipline, then that probably eliminates that advantage, but as a team grows it gets progressively harder to keep disciplined, just as it becomes more important to maintain module boundaries.

This advantage becomes a handicap if you don't get your boundaries right. This is one of the two main reasons for a Monolith First strategy, and why even those more inclined to run with microservices early stress that you can only do so with a well understood domain.

But I'm not done with caveats on this point yet. You can only really tell how well a system has maintained modularity after time has passed. So we can only really assess whether microservices lead to better modularity once we see microservice systems that have been around for at least a few years. Furthermore, early adopters tend to be more talented, so there's a further delay before we can assess the modularity advantages of microservice systems written by average teams. Even then, we have to accept that average teams write average software, so rather than compare the results to top teams we have to compare the resulting software to what it would have been under a monolithic architecture - which is a tricky counter-factual to assess.

All I can go on for the moment is the early evidence I have hear from people I know who have been using this style. Their judgement is that it is significantly easier to maintain their modules.

Sam Newman's book is the key resource if you want to find out more about how to build a microservice system.

One case study was particularly interesting. The team had made the wrong choice, using microservices on a system that wasn't complex enough to cover the Microservice Premium. The project got in trouble and needed to be rescued, so lots more people were thrown onto the project. At this point the microservice architecture became helpful, because the system was able to absorb the rapid influx of developers and the team was able to leverage the larger team numbers much more easily than is typical with a monolith. As a result the project accelerated to a productivity greater than would have been expected with a monolith, enabling the team to catch up. The result was still a net negative, in that the software cost more staff-hours than it would have done if they had gone with a monolith, but the microservices architecture did support ramp up.

Distribution (Con)

So microservices use a distributed system to improve modularity. But distributed software has a major disadvantage, the fact that it's distributed. As soon as you play the distribution card, you incur a whole host of complexities. I don't think the microservice community is as naive about these costs as the distributed objects movement was, but the complexities still remain.

The first of these is performance. You have to be in a really unusual spot to see in-process function calls turn into a performance hot spot these days, but remote calls are slow. If your service calls half-a-dozen remote services, each which calls another half-a-dozen remote services, these response times add up to some horrible latency characteristics.

Of course you can do a great deal to mitigate this problem. Firstly you can increase the granularity of your calls, so you make fewer of them. This complicates your programming model, you now have to think of how to batch up your inter-service interactions. It will also only get you so far, as you are going to have to call each collaborating service at least once.

The second mitigation is to use asynchrony. If make six asynchronous calls in parallel you're now only as slow as the slowest call instead of the sum of their latencies. This can be a big performance gain, but comes at another cognitive cost. Asynchronous programming is hard: hard to get right, and much harder to debug. But most microservice stories I've heard need asynchrony in order to get acceptable performance.

Right after speed is reliability. You expect in-process function calls to work, but a remote call can fail at any time. With lots of microservices, there's even more potential failure points. Wise developers know this and design for failure. Happily the kinds of tactics you need for asynchronous collaboration also fit well with handling failure and the result can improve resiliency. That's not much compensation however, you still have the extra complexity of figuring out the consequences of failure for every remote call.

And that's just the top two Fallacies of Distributed Computing.

There are some caveats to this problem. Firstly many of these issues crop up with a monolith as it grows. Few monoliths are truly self-contained, usually there are other systems, often legacy systems, to work with. Interacting with them involves going over the network and running into these same problems. This is why many people are inclined to move more quickly to microservices to handle the interaction with remote systems. This issue is also one where experience helps, a more skillful team will be better able to deal with the problems of distribution.

But distribution is always a cost. I'm always reluctant to play the distribution card, and think too many people go distributed too quickly because they underestimate the problems.

Eventual Consistency (Con)

I'm sure you know websites that need a little patience. You make an update to something, it refreshes your screen and the update is missing. You wait a minute or two, hit refresh, and there it is.

This is a very irritating usability problem, and is almost certainly due to the perils of eventual consistency. Your update was received by the pink node, but your get request was handled by the green node. Until the green node gets its update from pink, you're stuck in an inconsistency window. Eventually it will be consistent, but until then you're wondering if something has gone wrong.

Inconsistencies like this are irritating enough, but they can be much more serious. Business logic can end up making decisions on inconsistent information, when this happens it can be extremely hard to diagnose what went wrong because any investigation will occur long after the inconsistency window has closed.

Microservices introduce eventual consistency issues because of their laudable insistence on decentralized data management. With a monolith, you can update a bunch of things together in a single transaction. Microservices require multiple resources to update, and distributed transactions are frowned on (for good reason). So now, developers need to be aware of consistency issues, and figure out how to detect when things are out of sync before doing anything the code will regret.

The monolithic world isn't free from these problems. As systems grow, there's more of a need to use caching to improve performance, and cache invalidation is the other Hard Problem. Most applications need offline locks to avoid long-lived database transactions. External systems need updates that cannot be coordinated with a transaction manager. Business processes are often more tolerant of inconsistencies than you think, because businesses often prize availability more (business processes have long had an instinctive understanding of the CAP theorem).

So like with other distributed issues, monoliths don't entirely avoid inconsistency problems, but they do suffer from them much less, particularly when they are smaller.

Independent Deployment (Pro)

The trade-offs between modular boundaries and the complexities of distributed systems have been around for my whole career in this business. But one thing that's changed noticeably, just in the last decade, is the role of releasing to production. In the twentieth century production releases were almost universally a painful and rare event, with day/night weekend shifts to get some awkward piece of software to where it could do something useful. But these days, skillful teams release frequently to production, many organizations practicing Continuous Delivery, allowing them to do production releases many times a day.

Microservices are the first post DevOps revolution architecture

-- Neal Ford

This shift has had a profound effect on the software industry, and it is deeply intertwined with the microservice movement. Several microservice efforts were triggered by the difficulty of deploying large monoliths, where a small change in part of the monolith could cause the whole deployment to fail. A key principle of microservices is that services are components and thus are independently deployable. So now when you make a change, you only have to test and deploy a small service. If you mess it up, you won't bring down the entire system. After all, due the need to design for failure, even a complete failure of your component shouldn't stop other parts of the system from working, albeit with some form of graceful degradation.

This relationship is a two-way street. With many microservices needing to deploy frequently, it's essential you have your deployment act together. That's why rapid application deployment and rapid provisioning of infrastructure are Microservice Prerequisites. For anything beyond the basics, you need to be doing continuous delivery.

The great benefit of continuous delivery is the reduction in cycle-time between an idea and running software. Organizations that do this can respond quickly to market changes, and introduce new features faster than their competition.

Although many people cite continuous delivery as a reason to use microservices, it's essential to mention that even large monoliths can be delivered continuously too. Facebook and Etsy are the two best known cases. There are also plenty of cases where attempted microservices architectures fail at independent deployment, where multiple services need their releases to be carefully coordinated [2]. While I do hear plenty of people arguing that it's much easier to do continuous delivery with microservices, I'm less convinced of this than their practical importance for modularity - although naturally modularity does correlate strongly with delivery speed.

Operational Complexity (Con)

Being able to swiftly deploy small independent units is a great boon for development, but it puts additional strain on operations as half-a-dozen applications now turn into hundreds of little microservices. Many organizations will find the difficulty of handling such a swarm of rapidly changing tools to be prohibitive.

This reinforces the important role of continuous delivery. While continuous delivery is a valuable skill for monoliths, one that's almost always worth the effort to get, it becomes essential for a serious microservices setup. There's just no way to handle dozens of services without the automation and collaboration that continuous delivery fosters. Operational complexity is also increased due to the increased demands on managing these services and monitoring. Again a level of maturity that is useful for monolithic applications becomes necessary if microservices are in the mix.

Microservice proponents like to point out that since each service is smaller it's easier to understand. But the danger is that complexity isn't eliminated, it's merely shifted around to the interconnections between services. This can then surface as increased operational complexity, such as the difficulties in debugging behavior that spans services. Good choices of service boundaries will reduce this problem, but boundaries in the wrong place makes it much worse.

Handling this operational complexity requires a host of new skills and tools - with the greatest emphasis being on the skills. Tooling is still immature, but my instinct tells me that even with better tooling, the low bar for skill is higher in a microservice environment.

Yet this need for better skills and tooling isn't the hardest part of handling these operational complexities. To do all this effectively you also need to introduce a devops culture: greater collaboration between developers, operations, and everyone else involved in software delivery. Cultural change is difficult, especially in larger and older organizations. If you don't make this up-skilling and cultural change, your monolithic applications will be hampered, but your microservice applications will be traumatized.

Technology Diversity (Pro)

Since each microservice is an independently deployable unit, you have considerable freedom in your technology choices within it. Microservices can be written in different languages, use different libraries, and use different data stores. This allows teams to choose an appropriate tool for the job, some languages and libraries are better suited for certain kinds of problems.

Discussion of technical diversity often centers on best tool for the job, but often the biggest benefit of microservices is the more prosaic issue of versioning. In a monolith you can only use a single version of a library, a situation that often leads to problematic upgrades. One part of the system may require an upgrade to use its new features but cannot because the upgrade breaks another part of the system. Dealing with library versioning issues is one of those problems that gets exponentially harder as the code base gets bigger.

There is a danger here that there is so much technology diversity that the development organization can get overwhelmed. Most organizations I know do encourage a limited set of technologies. This encouragement is supported by supplying common tools for such things as monitoring that make it easier for services to stick to a small portfolio of common environments.

Don't underestimate the value of supporting experimentation. With a monolithic system, early decisions on languages and frameworks are difficult to reverse. After a decade or so such decisions can lock teams into awkward technologies. Microservices allow teams to experiment with new tools, and also to gradually migrate systems one service at a time should a superior technology become relevant.

Secondary Factors

I see the items as above as the primary trade-offs to think about. Here's a couple more things that come up that I think are less important.

Microservice proponents often say that services are easier to scale, since if one service gets a lot of load you can scale just it, rather than the entire application. However I'm struggling to recall a decent experience report that convinced me that it was actually more efficient to do this selective scaling compared to doing cookie-cutter scaling by copying the full application.

Microservices allow you to separate sensitive data and add more careful security to that data. Furthermore by ensuring all traffic between microservices is secured, a microservices approach could make it harder to exploit a break-in. As security issues grow in importance, this could migrate to becoming a major consideration for using microservices. Even without that, it's a not unusual for primarily monolithic systems to create separate services to handle sensitive data.

Critics of microservices talk about the greater difficulty in testing a microservices application than a monolith. While this is a true difficulty - part of the greater complexity of a distributed application - there are good approaches to testing with microservices. The most important thing here is to have the discipline to take testing seriously, compared to that the differences between testing monoliths and testing microservices are secondary.

Summing Up

Any general post on any architectural style suffers from the Limitations Of General Advice. So reading a post like this can't lay out the decision for you, but such articles can help ensure you consider the various factors that you should take into account. Each cost and benefit here will have a different weight for different systems, even swapping between cost and benefit (strong module boundaries are good in more complex systems, but a handicap to simple ones) Any decision you make depends on applying such criteria to your context, assessing which factors matter most for your system and how they impact your particular context. Furthermore, our experience of microservice architectures is relatively limited. You can usually only judge architectural decisions after a system has matured and you've learned what it's like to work with years after development began. We don't have many anecdotes yet about long-lived microservice architectures.

Microservice Resource Guide

For more information about microservices, start with my Microservice Resource Guide, where I've selected the best information on the what, when, how, and who of microservices.

Monoliths and microservices are not a simple binary choice. Both are fuzzy definitions that mean many systems would lie in a blurred boundary area. There's also other systems that don't fit into either category. Most people, including myself, talk about microservices in contrast to monoliths because it makes sense to contrast them with the more common style, but we must remember that there are systems out there that don't fit comfortably into either category. I think of monoliths and microservcies as two regions in the space of architectures. They are worth naming because they have interesting characteristics that are useful to discuss, but no sensible architect treats them as a comprehensive partitioning of the architectural space.

That said, one general summary point that seems to be widely accepted is there is a Microservice Premium: microservices impose a cost on productivity that can only be made up for in more complex systems. So if you can manage your system's complexity with a monolithic architecture then you shouldn't be using microservices.

But the volume of the microservices conversation should not let us forget the more important issues that drive the success and failure of software projects. Soft factors such as the quality of people on the team, how well they collaborate with each other, and the degree of communication with domain experts, will have a bigger impact than whether to use microservices or not. On a purely technical level, it's more important to focus on things like clean code, good testing, and attention to evolutionary architecture.

Footnotes

1: Some people consider "monolith" to be an insult, always implying poor modular structure. Most people in the microservices world don't do this, they define "monolith" purely as an application built as a single unit. Certainly microservices-advocates believe that most monoliths end up being Big Balls of Mud, but I don't know any who would argue that it's impossible to built a well-structured monolith.

2: The ability to deploy services independently is part of the definition of microservices. So it's reasonable to say that a suite of services that must have its deployments coordinated is not a microservice architecture. It is also reasonable to say that many teams that attempt a microservice architecture get into trouble because they end up having to coordinate service deployments.

Further Reading

Sam Newman gives his list of the benefits of microservices in Chapter 1 of his book (the essential source for details of building a microservices system).

Benjamin Wootton's post, Microservices - Not A Free Lunch! on High Scaleability, was one of the earliest, and best, summaries of the downsides of using microservices.

Acknowledgements

Brian Mason, Chris Ford, Rebecca Parsons, Rob Miles, Scott Robinson, Stefan Tilkov, Steven Lowe, and Unmesh Joshi discussed drafts of this article with me.