What microservices are — and when they help
A microservices architecture splits an application into small services, each owning one business capability and its own data, communicating through APIs or events. Teams can develop, deploy and scale their services independently, and a failure in one service need not take down the whole system.
Those benefits come with real costs: distributed systems are harder to test, monitor and keep consistent. Microservices are the right choice when several teams need to work independently, when parts of the system have very different scaling needs, or when a monolith has become a bottleneck for delivery. For a small team building a new product, a well-structured modular monolith is usually better.
Problems we solve
- A monolith where every change requires coordinating several teams
- One busy feature forcing the whole application to scale
- Deployments of the entire system for a small change
- An existing microservices system that is hard to debug and keep consistent
- Unclear service boundaries creating chatty, tightly coupled services
What’s included
Architecture & domain design
Service boundaries based on business capabilities using domain-driven design.
Service implementation
Services in Node.js, Go, Java, .NET or Python with clear contracts.
Event-driven communication
Kafka, RabbitMQ or cloud queues for decoupled, resilient workflows.
API gateway & security
Central authentication, rate limiting and routing.
Observability
Distributed tracing, correlated logs and service dashboards.
Monolith decomposition
Gradual extraction of services using the strangler pattern.
Decomposing a monolith safely
-
Map the domain2–3 weeks
Identify business capabilities, data ownership and the parts that change or scale most.
-
Modularise firstOngoing
Create clear module boundaries inside the monolith before extracting anything.
-
Extract one service4–8 weeks
Move the highest-value capability out, with its data and a stable API.
-
Repeat and retireMonths
Continue capability by capability while the monolith shrinks.
Data and consistency
The hardest part of microservices is data. Each service should own its data, which means no shared database tables and no distributed transactions across services. We use patterns such as events to propagate changes, the outbox pattern to publish events reliably, sagas for multi-step business processes and read models for queries that span services. These patterns keep the system consistent without tight coupling.
Synchronous or asynchronous communication?
| Style | Strengths | Risks |
|---|---|---|
| REST or gRPC calls | Simple, immediate responses | Chains of calls multiply latency and failure |
| Events and message queues | Loose coupling, resilience, natural audit trail | Eventual consistency and harder debugging |
Most systems use both: synchronous calls for queries that need an immediate answer and events for state changes other services react to, such as “order placed” or “payment received”.
The modular monolith alternative
Many teams adopt microservices for problems a well-structured monolith would solve more cheaply. A modular monolith keeps one deployable application but enforces strict boundaries between modules, each with its own data and public interface. It delivers much of the maintainability benefit without network calls, distributed transactions or a fleet of deployments — and modules can be extracted into services later if scaling or team independence demands it.
- Choose microservices when separate teams need to deploy independently
- Or when components have very different scaling or technology needs
- Otherwise, start with a modular monolith and clear boundaries
Operational prerequisites
- Automated CI/CD for every service
- Centralised logging, metrics and distributed tracing
- Service discovery, API gateway and consistent authentication
- Versioned APIs and contract tests between services
- Teams that own services end to end, including on-call
Monolith, modular monolith or microservices?
| Monolith | Modular monolith | Microservices | |
|---|---|---|---|
| Deployment | One unit | One unit with strict internal modules | Many independent services |
| Team size that fits | One small team | One to several teams | Several autonomous teams |
| Operational overhead | Low | Low | High: monitoring, networking, tracing |
| Independent scaling | No | Limited | Yes, per service |
| Typical risk | Tangled code over time | Module boundaries eroding | Distributed complexity, data consistency |
Designing service boundaries
Good boundaries follow business capabilities — orders, billing, inventory, notifications — rather than technical layers. Each service owns its data and exposes it only through APIs or events. We use domain-driven design workshops with your product and engineering leads to find natural boundaries, then validate them by asking how often a typical change would touch more than one service. If most changes cut across several services, the boundaries are wrong.
- Each service has one clear owner team
- Services do not share database tables
- A change to one capability usually touches one service
- Services can be deployed independently
- Failures in one service degrade gracefully rather than cascading
Observability for distributed systems
When a request passes through five services, finding the cause of a slow or failed request needs proper tooling. We set up observability from the first service, not after the first outage.
- Distributed tracing with OpenTelemetry so each request can be followed end to end
- Structured logs with correlation IDs
- Metrics and dashboards for latency, errors and saturation per service
- Alerts based on service-level objectives rather than raw CPU
- Health checks, timeouts, retries and circuit breakers configured consistently
Testing microservices
Testing changes too. Unit tests remain the foundation, but contract tests (for example with Pact) check that services still agree on API shapes without spinning up the whole system. A small number of end-to-end tests then cover the critical user journeys. This balance keeps pipelines fast while catching the integration breaks that microservices make easy to introduce.
Running services on Kubernetes? See Kubernetes consulting and site reliability engineering.
Signs your microservices need fixing
- Every release still has to deploy several services together
- Services call each other in long synchronous chains
- Several services read and write the same database tables
- Nobody can tell which service caused a failed request
- Local development requires running the entire system
- Infrastructure costs have grown faster than traffic
Consolidating services that should never have been split
Some teams split too early and end up with dozens of tiny services that always change together. Merging them back into fewer, larger services — or into a modular monolith — is a legitimate and often valuable step. We identify candidates by looking at change history, call patterns and shared data, then merge incrementally behind stable APIs so clients are unaffected.
Migrating a monolith step by step is closely related to legacy modernization and usually relies on CI/CD to release safely.
Team structure and ownership
Microservices work best when each team owns a small number of services end to end — code, deployment, monitoring and on-call. Without clear ownership, services drift and nobody feels responsible when they fail. We help define ownership, shared standards for logging, security and APIs, and lightweight governance such as an internal service catalogue, so autonomy does not turn into inconsistency.
Where the organisation is not yet ready for this model, we say so and recommend a modular monolith first; it keeps the door open to services later without paying the operational cost today.
That honesty saves money: the cheapest distributed system is often the one you do not build yet.
Related services
Microservices typically run on Kubernetes or other cloud-native platforms, and often come up during legacy modernization.
This page is part of Custom Software Development Services — see the full overview for related services, approach and pricing.