Guide contents

Lesson 54 of 129

When to Use Microservices

Microservices trade simplicity for independence. Each service can be developed, released, and scaled separately, but the system as a whole becomes a distributed system with the failure modes, consistency limits, and operational load that come with it. The decision to adopt them is justified only when the benefits address a problem the organization actually has.

Drivers

Team scale and independent deployment

The strongest driver is organizational: many teams working on one codebase. In a single deployable, every change is released together, so teams coordinate merges, test suites, and release schedules. As the number of contributors grows, this coordination cost rises and release frequency falls.

With independent services:

  • A team releases its service on its own schedule.
  • A failed release affects one service, and rollback is limited to that service.
  • Build and test time per service stays bounded as the total system grows.

The benefit exists only if services are truly independently deployable, which requires stable contracts and no lock-step releases (see the distributed monolith).

Independent scaling

When components have very different load profiles, replicating the whole application to scale one hot component wastes resources. Examples: an image-processing component that is CPU-bound, a search component that needs large memory, and an order component that is I/O-bound. Separate services can run on different instance types and scale with separate autoscaling policies.

Example: a monolith instance needs 8 GB of memory because of an in-memory search index, while the checkout code needs 200 MB. To handle 10 times more checkout traffic, the monolith runs 10 more 8 GB instances (80 GB), although 10 instances of a separate checkout service need about 2 GB in total. The gap is the cost of coupled scaling.

This driver matters only if the difference in resource profile is large. If load is roughly uniform, scaling a monolith horizontally behind a load balancer is simpler.

Technology heterogeneity

Different problems suit different runtimes and datastores: a recommendation service in Python next to a latency-sensitive service in Java, a graph database for one domain and a relational database for another. Service boundaries allow this. The cost is more toolchains to maintain, secure, and staff, so most organizations limit the number of supported stacks.

Fault isolation

A memory leak or crash loop in one component can take down a monolith entirely. Separate processes limit the blast radius, provided that callers handle the failure of dependencies with timeouts, circuit breakers, and fallbacks. Without that handling, a failure propagates through synchronous calls and isolation is lost.

Different change rates and compliance boundaries

  • Components that change daily can be released independently of components that change quarterly.
  • Components handling regulated data (payments, personal data) can be isolated with separate access controls, audit scope, and network segments, reducing the scope of compliance audits.

When not to use microservices

Situation Reason
Small team (roughly one team) No coordination cost to remove; the operational overhead is pure cost
Domain not yet understood Boundaries drawn early are often wrong, and moving a boundary across services is far costlier than across modules
Early-stage product with rapid pivots Requirements shift across boundaries; cross-service refactoring is slow
Strong cross-entity transactional needs Invariants spanning entities need ACID; across services they become sagas and eventual consistency
No platform capability Without CI/CD, container orchestration, observability, and on-call practice, each new service adds manual work
Uniform load and low scale A replicated monolith meets the demand at lower cost

A common failure is a premature split: services created before the domain model is stable. They then change together, which produces the coupling of a monolith with the costs of a network.

Prerequisites

Before extracting services, an organization typically needs:

Decision criteria

flowchart TD
    A["Multiple teams blocked by shared releases?"] -->|No| M["Stay with a monolith or modular monolith"]
    A -->|Yes| B["Domain boundaries stable and understood?"]
    B -->|No| M
    B -->|Yes| C["Platform and observability in place?"]
    C -->|No| D["Build platform capability first; use a modular monolith meanwhile"]
    C -->|Yes| E["Extract services along stable boundaries, starting with the highest-value ones"]

Questions that support the decision:

  1. Are teams waiting on each other to release? If not, the main driver is absent.
  2. Can the domain be divided so that most changes touch one service? If most features require coordinated changes to several, the boundaries are wrong or the domain is not divisible yet.
  3. Can the business accept eventual consistency where transactions used to be atomic?
  4. Is there a specific component with a distinct scaling, security, or availability requirement?
  5. Can the organization operate many deployable units (pipelines, dashboards, alerts, on-call)?

An incremental path is common: build a modular monolith, then extract the modules that meet one of the drivers, using the approach in migrating to microservices.

Interview questions

  • What is the main reason to adopt microservices? Independent deployment by multiple teams. It removes release coordination across teams, which is an organizational limit rather than a purely technical one.
  • Why is starting with microservices risky for a new product? The domain model and its boundaries are not yet known. Wrong boundaries cause coordinated changes across services, and correcting them is more costly than moving code between modules of one application.
  • When is independent scaling a valid driver? When components have significantly different resource profiles or load patterns, so that replicating the whole application to scale one part wastes resources.
  • What must be in place before adopting microservices? Automated CI/CD, container orchestration, centralized observability, service discovery, and teams prepared to own services in production.