Fundamentals
The opening questions. The strongest answers here are balanced — anyone can list the benefits, but naming the costs and saying when not to use microservices is what marks out experience.
What are microservices?
One application split into small, independently deployable services, each owning a single business capability and its own data.
The defining characteristics, in the order they matter:
- Independently deployable — if you can't ship one without shipping the others, it isn't a microservice architecture.
- Owns its data — no other service reads its tables.
- Organised around a business capability, not a technical layer.
- Communicates over the network — which is the source of every hard problem in this tutorial.
Microservices split an application into small services, each owning one business capability
and its own data store, each independently deployable and scalable, communicating over the
network. The two properties that actually define it are independent deployability and private
data — if services share a database or have to be released together, you have a distributed
monolith with all the cost and none of the benefit.
Monolith vs microservices
| Monolith | Microservices | |
|---|---|---|
| Deployment | One unit — all or nothing | Per service |
| Database | One, shared | One per service |
| Communication | In-process method calls | Network — HTTP, gRPC, messages |
| Transactions | ACID, trivially | Saga + eventual consistency |
| Scaling | The whole app, together | Only the service that needs it |
| Failure | One bug can take everything down | Isolated — if you designed for it |
| Debugging | One stack trace | Distributed tracing across services |
| Team | Everyone in one codebase | One team owns one service |
| Operational cost | Low | High — CI/CD, monitoring, infra per service |
"They solve an organisational and scaling problem, not a coding problem. If one team ships a well-structured monolith fortnightly with no bottleneck, microservices will make everything slower. The right starting point is usually a modular monolith with clean internal boundaries — then split out the piece that genuinely needs independent scaling or ownership."
- Independent deployment — fix the payment service without redeploying the catalogue.
- Independent scaling — run twelve instances of the service under load and one of the rest.
- Team autonomy — one team owns a service end to end and sets its own release pace.
- Fault isolation — a failing recommendation service degrades a feature instead of taking down checkout.
- Technology freedom — the right language or database per service (used sparingly; a zoo of stacks is its own cost).
- Smaller blast radius per change and a codebase a new joiner can actually read.
What are the disadvantages?
Every call that used to be a method call is now a network call — and you inherit latency, partial failure and distributed data along with it.
| Cost | What it means day to day |
|---|---|
| Partial failure | A call can succeed, fail, or time out with no answer at all — that third case is what makes idempotency mandatory (Module 04) |
| Latency | Microseconds become milliseconds, and a chain of five calls compounds it |
| No distributed ACID | Saga, compensations, eventual consistency (Module 05) |
| Debugging | You need correlation ids and distributed tracing just to answer "where did it go wrong?" |
| Operational burden | Per service: pipeline, monitoring, alerting, secrets, on-call |
| Contract versioning | You can't deploy a breaking change atomically across consumers |
| Testing | Integration testing is genuinely hard; contract tests become essential |
| Data duplication | The same customer name lives in three services and must be kept in step |
The worst outcome, and the most common: services that are split on paper but share a database, must be deployed together, and call each other synchronously in a chain. You pay every cost of distribution and receive none of the benefits. If asked "what goes wrong in practice?", this is the answer.
When should you NOT use microservices?
| Situation | Why not |
|---|---|
| Small team | Five developers running twelve services spend their time on infrastructure, not features |
| The domain isn't understood yet | Boundaries drawn early are drawn wrong, and moving a boundary between services is far harder than moving a class |
| No CI/CD, monitoring or on-call | Microservices are an operations bet — without the platform they fail |
| Modest scale | One well-built monolith on decent hardware serves an enormous number of users |
| Heavily transactional core | If nearly every operation needs one ACID transaction, splitting it means writing sagas for routine work |
| "Because it's modern" | Not a reason |
I'd avoid them for a small team, an unclear domain, or where the organisation has no CI/CD
and monitoring maturity — microservices are as much an operational commitment as an
architectural one. My default is a modular monolith with strict internal boundaries, then
extracting a service when there's a concrete reason: it needs to scale differently, it needs a
different release cadence, or a separate team should own it. That way the boundary is proven
before it becomes a network call.
What is database per service?
Each service owns its schema, and no other service touches it — all access goes through that service's API or its events.
It's the rule that makes everything else possible. Without it you can't deploy independently, so you don't really have microservices.
"How do I join across services then?" You don't. You either compose at the API level (Q42), or you keep a read model built from events. And often the join isn't needed: the Order service can store the customer's name at the time of the order, which is what it actually wants anyway.
- Independent schema evolution — rename a column without a cross-team release.
- Encapsulation — the service's internals stay internal, so its API is the only contract to maintain.
- Right tool per service — SQL Server for orders, Redis for sessions, a document store for the catalogue.
- Failure isolation — one service's runaway query doesn't lock the tables of another.
- Independent scaling of the data tier as well as the service.
Technically yes — and during a migration out of a monolith it's often a deliberate intermediate step. As a target design it's an anti-pattern, because it removes the independence that justified the split.
| Acceptable | Not acceptable |
|---|---|
| A temporary migration stage, with a plan to split | Two services writing the same tables |
| A read-only replica or reporting warehouse | One service reading another's tables directly |
| Separate schemas in one server, with strict ownership | Shared write models "to avoid duplication" |
If you say "yes, during a migration, with a plan to separate", that reads as pragmatic. If you say "yes, it's fine", it reads as not having lived with the consequences.