“Should we use microservices?” is one of the most common architecture questions, and the honest answer for most teams is not yet. Microservices solve real problems, but those problems are mostly about large organizations, and they bring a lot of operational complexity. Here’s how the options compare, and how to decide.

The options

Architecture What it is
Monolith One application, one codebase, deployed as one unit, usually one database
Modular monolith One deployable, internally split into well-separated modules with clear interfaces
Microservices Many small services, each deployed independently, each owning its data, talking over the network

Comparison

Monolith Microservices
Development start Fast Slow (infrastructure first)
Deployment One artifact Many pipelines, versioning between services
Local development Run one app Run many services (or mocks)
Calls between parts In-process function calls Network calls: latency, retries, failures
Data consistency Database transactions Eventual consistency, sagas, events
Scaling Scale the whole app Scale each service independently
Fault isolation One bug can take everything down Failures can be contained (if designed well)
Team independence Coordination needed Teams own and release services independently
Operations One app to monitor Service discovery, tracing, many dashboards
Hosting cost Low Higher (more processes, more infrastructure)

Why monoliths are a good default

  • Simplicity: one codebase to understand, one deployment, one database transaction.
  • Performance: function calls are far faster than network calls.
  • Easy refactoring: moving code between modules is an IDE operation, not an API migration.
  • Cheap hosting: a single VPS (plus a database) can serve a lot of traffic with caching. See web application architecture.

Many large, successful products run as (modular) monoliths. Horizontal scaling still works: run several copies of the monolith behind a load balancer. See Nginx load balancing.

When microservices make sense

  • Many teams (dozens of engineers) who block each other’s releases in one codebase.
  • Parts with very different scaling needs (a video transcoding service vs a small admin panel).
  • Parts needing different technologies (a Python ML service next to a Go API).
  • Fault isolation requirements: a failing recommendation engine mustn’t break checkout.
  • You already have mature CI/CD, monitoring, tracing and on-call practices.

If none of these apply, microservices mostly add cost.

The middle path: a modular monolith

Structure the monolith into modules with clear boundaries (orders, billing, users, catalog), each with its own internal API and its own tables, and rules against reaching into another module’s internals. You keep the simplicity of one deployment, and if a module ever needs to become a service, the boundary already exists.

Extracting a service step by step

When a real reason appears:

  1. Pick one module with a clear boundary and a reason to separate (scaling, team, technology).
  2. Put an API in front of it inside the monolith first.
  3. Give it its own data store, and migrate data access through the API.
  4. Deploy it separately, and route calls over the network (HTTP/gRPC, or a queue for async work).
  5. Add monitoring, tracing and error tracking for the new service. See OpenTelemetry and error tracking with Sentry.

This is the “strangler fig” approach: grow new services around the old system, without a risky big rewrite.

What microservices require

Need Typical tools
Containers and orchestration Docker, Kubernetes or k3s, Nomad. See k3s
Service-to-service communication HTTP/gRPC, message queues. See background jobs and queues
Configuration and secrets Environment variables, Vault, Kubernetes secrets
Observability Centralized logs, metrics, distributed tracing
Deployment CI/CD per service, versioned APIs
Resilience Timeouts, retries, circuit breakers

Hosting on VPSs

  • Monolith: one VPS (or a few behind a load balancer), plus a database server. Simple and cost-efficient.
  • A few services: Docker Compose on one or two VPSs works well. See Kubernetes vs Docker Compose.
  • Many services: a small Kubernetes cluster (k3s) across several VPSs, with a private network between them.

Decision guide

Situation Choose
New product, small team (< 10 devs) Monolith, structured as modules
Growing team, release conflicts Modular monolith, extract hotspots
Many teams, mature DevOps, distinct scaling needs Microservices
Unsure Monolith: splitting later is easier than merging

Related: Docker Compose in production and deployment strategies.

  • Architecture
  • DevOps
  • Microservices