What are Microservices?
The availability of cloud platforms, virtualization advancements, and the emergence of agile and DevOps practices has thoroughly upended traditional software development practices. Not only that, but traditional monolithic applications (including their supporting infrastructure) were not built to take advantage of flexible cloud resources.
Microservices, as an architecture, describes an approach to building complex applications using independent, replaceable processes. These processes are single-purpose, with a small scope. They communicate with other services using lightweight APIs and language-agnostic protocols. These independent processes are also called microservices.
Separating the bits of an application into pieces can be very freeing, and it is easy to appreciate from a quick skim how isolating each piece of an application would reduce the overhead involved in making changes, and how it would reduce the risk around trying something new. But this approach does not come without pitfalls of its own: distributed systems are not a trivial problem space to build in.
There is no magic around microservices. These tidy little services can become a spaghetti mess just like software built with any other approach. Both thinking and team organization need to change to make microservices successful.
Microservices are independent. Agility is one of the benefits of microservice architectures, but it only exists when services are capable of being completely re-written without disturbing other services. That isn’t likely to happen often, but it explains the requirement. Clear API boundaries give the team working on a service the most flexibility to evolve the implementation. This characteristic is what enables polyglot programming and persistence.
Microservices are resilient. Overall application stability depends on individual microservices being robust to failure. This is a big difference from traditional architectures, where the supporting infrastructure handled failures for you. Each service needs to apply isolation patterns like circuit breakers and bulkheads to contain downstream failures and define appropriate fallback behaviors to protect upstream services.
Microservices are stateless, transient processes. This is not the same as saying microservices can’t have state! It does mean that state should be stored in external backing cloud services, like Redis, rather than in-memory. Fast startup and graceful shutdown behavior further allows microservices to work well with automated orchestrators that create and destroy instances in response to load.
By definition, applications that use microservices have lots of moving parts. Automated tools make the difference between a flexible, scalable, and yet stable system, and an unmanagable mess.
Continuous integration and deployment (CI/CD) pipelines are the base upon which a microservices application is built. Unique pipelines per service ensures that services can evolve at their own pace. The ideal is for any given built instance to transition through stages of a pipeline for eventual deployment into production without requiring any code changes along the way. To make this possible, the built artifact must be self-contained, which includes using explicitly declared dependencies and maintaining a firm line between the application-specific configuration that is built into the artifact and the environment-specific configuration that is injected later.
Containers are a big help with CI/CD, as they naturally define the entire operating environment for the service. Using containers goes a long way towards eliminating those hard-to-spot differences between development and production environments, which are a common source of last-minute deployment surprises.
Different services will have different scaling requirements: a catalog service on black friday is going to use resources very differently than an account service, for example. CI/CD pipelines perform a role in defining per-service scaling policies as they automate deployment into different environments. Orchestration can then use those policies to manage routing and load balancing accordingly.
There is a huge cultural component to microservices. Conway’s law is often cited for a valid reason: organizational structure does influence how software is built.
As an example, if an organization is divided so that application developers are separate from database administrators, the resulting architecture will have distinct tiers: a tier optimized for application code, and a data tier that is optimal for database administrators. Application changes that require database changes have to then be coordinated between the two distinct groups. That should sound familiar.
To build truly independent microservices, each service needs to be owned, maintained, and operated by a single team. From cradle to grave and at all stages in-between, that team owns the details of how the service works. There will be some coordination required with other teams, but that coordination should be at the surface level. For the curious, this is where domain-driven design enters the conversation. Clearly defined, versioned APIs that use language-agnostic protocols will go a long way to keeping coordination required between teams focused on the important external characteristics of the service, and away from the internal details, which should remain free to change at any time.
There is a lot to learn, more than is obvious on the surface. Linked to this page are references: a youtube video series, a whitepaper, and three redbooks, all of which go into a lot more depth on various aspects of microservice architectures.
The IBM Cloud Garage Method incorporates best practices that span the entire space, from team formation, to devops, and in-depth information about microservices and related architectures and technologies. Use these resources to jumpstart your transition to microservices and cloud computing.
IBM supports Cloud Native development and makes it easy to get started