Architecture 3 min read

Microservices: the stack, when to adopt, and when to avoid

A practical guide to distributed systems architecture. Microservices solve real problems at scale, and create expensive ones for teams that adopt them a year too early.

A modular steel lattice, the same unit repeated and bolted into one structure
In this guide
  1. 1. When should you adopt microservices?
  2. 2. When should you avoid microservices?
  3. 3. The modern microservices stack

Microservices architecture has become the default answer for high scale enterprise applications. Breaking a large, unified codebase into autonomous, loosely coupled services lets engineering teams build, deploy and scale parts of a system independently of each other.

It also introduces distributed system complexity, networking overhead and operational friction, enough of all three to cripple a team that adopted it before it needed to. The interesting question is not how to build microservices. It is whether you should.

1. When should you adopt microservices?

Four green lights. You want more than one of them.

  • High team velocity and multi team ownership. Twenty or more engineers divided into distinct teams, billing, search, inventory. Microservices remove the deployment bottleneck a shared codebase creates.
  • Heterogeneous scaling needs. Parts of the application need vastly different resources: an AI processing pipeline wants heavy GPU compute, while an order management service wants low latency and high database IOPS. Scaling those together means over provisioning one to serve the other.
  • Domain autonomy and tech diversity. Different domains genuinely need different databases or languages, Python for AI and ML, Go for high throughput gateway routes, Node.js for real time WebSockets.
  • Fault isolation is critical. A crash in a non core service, email notifications, analytics generation, must never take down checkout or authentication.

2. When should you avoid microservices?

Four red flags. One of these is enough to stay on a monolith.

  • Early stage startup, unclear domain boundaries. If your product's domain boundaries are still moving, refactoring a service boundary is exponentially harder than refactoring code inside one codebase. You will be paying network latency to move a function between two files.
  • Small engineering team, under ten developers. Managing CI/CD pipelines, container orchestration, distributed tracing and a service mesh will take more of the week than the product does.
  • Low traffic and homogeneous load. If the application runs comfortably on a single server, or on scaled monolithic cloud instances, a modular monolith in Next.js or Laravel, microservices are over engineering with an ops bill attached.
  • Limited DevOps expertise. Without engineers who know Kubernetes, observability and network security properly, a distributed system does not fail gracefully. It fails at 2am, in a way nobody can trace.

3. The modern microservices stack

If the green lights are real and the red flags are not, this is the 2026 standard for each layer.

Component / layer Industry standard Purpose and core advantage
API gateway Kong / Envoy / Traefik Entry point for client traffic. Rate limiting, authentication, SSL termination and request routing.
Inter service communication gRPC / REST over HTTP/2 High performance, strongly typed binary RPC for service to service calls.
Event bus / async messaging Apache Kafka / RabbitMQ Decouples services through event driven pub/sub, trading immediate consistency for eventual consistency.
Container orchestration Kubernetes (EKS/GKE) / Nomad Automates deployment, auto scaling, self healing and container management across cluster nodes.
Service mesh Istio / Linkerd Service to service traffic, mutual TLS encryption, traffic splitting and retries.
Observability and tracing OpenTelemetry + Jaeger + Prometheus + Grafana Distributed tracing across asynchronous boundaries, central logging, and system metrics.

Read that table again as a list of things somebody has to operate. Six layers, each with its own failure modes, upgrade path and on call implications. That is the actual cost of the architecture, and it is charged whether or not you are getting the benefit.

The honest default for most teams is a modular monolith with clean internal boundaries, one deployable, one database, and the seams drawn where the services would be. When a boundary starts hurting, extract that one. Adopting the whole architecture in advance, on the grounds that you might need it, is how teams end up paying enterprise operational costs on startup traffic.

Deciding how to structure a system?

We would rather keep an architecture simple until something forces the change. If you are weighing it up for a real product, talk to us.

Custom software development Web application development
All articles