One-liner: A monolith is one deployable with in-process calls; microservices are independently deployable services owning their data; split for organizational scale, not for fun.
Monolith: [ web | orders | billing | users ] -> one PostgresModular monolith: [ web ]->[ orders ]->[ billing ] same deploy, module APIs, separate schemasMicroservices: [orders svc]--HTTP/gRPC/events-->[billing svc] each with its own DB
Splitting checklist:
Identify bounded contexts; find seams where data and change rate differ.
Enforce module boundaries inside the monolith first (separate schemas, no cross-module table access).
Extract the module with the most independent scaling/deploy need; strangler fig behind the gateway.
Replace in-process calls with sync RPC for queries, events for notifications; add outbox for reliability.
Add the platform: CI/CD per service, observability (tracing), service discovery (Consul/Kubernetes DNS), gateway.
In-process call: ~nanoseconds to microseconds. Same-DC RPC: ~0.5-2 ms plus serialization. A request chain of 10 services adds ~10-20 ms and multiplies failure probability.
10 services at 99.9% in series ≈ 99.0% without resilience patterns.
Rule of thumb: don't split below a team of ~8-10 engineers; a service should be rewritable in two weeks (small enough to own).