Deployment
One artifact to build, test, and ship. Simple, but every change redeploys the whole thing.
Each service deploys on its own cadence. Independent, but you now run many pipelines and versions.
Start with a monolith. For almost every team, a well-structured ("modular") monolith ships faster, is far easier to operate, and is simpler to reason about. Move to microservices when specific pressures force it: independent team scaling, components with wildly different scaling profiles, or a codebase too large to deploy as one unit. Microservices trade code complexity for distributed-systems complexity — make that trade deliberately, not by default.
This is an organizational decision dressed as a technical one. A monolith optimizes for simplicity: one repo, one deploy, in-process calls, easy transactions. Microservices optimize for autonomy: teams ship independently and scale services separately, at the cost of network calls, partial failure, and a lot of operational machinery. The famous advice — "you must be this tall to ride" — still holds.
One artifact to build, test, and ship. Simple, but every change redeploys the whole thing.
Each service deploys on its own cadence. Independent, but you now run many pipelines and versions.
In the codebase: risk of tangled modules if boundaries are not enforced.
In the network: service discovery, retries, timeouts, partial failure, eventual consistency.
Scale the whole app together, even if only one part is hot.
Scale each service independently to its own load.
Shared database, in-process ACID transactions across entities.
A database per service; cross-service consistency needs sagas or outbox patterns.
Teams share a codebase and coordinate merges and releases.
Teams own services end to end and ship without blocking each other.
A bug can take down the whole process, but there is no partial-failure ambiguity.
Faults isolate to a service, but the network introduces timeouts, retries, and cascading failures.
Run and step through the whole app locally; stack traces span the codebase.
Needs distributed tracing; reproducing a flow locally can mean spinning up many services.
A new product still finding its shape
Optimize for speed of change. A modular monolith keeps the door open to extract services later.
A large org with many autonomous teams
Independent deployability and ownership are worth the distributed-systems tax at this size.
One component needs 50× the resources of the rest
Extract just that component so you can scale it without scaling everything.
A team feeling slowed by a tangled codebase
Fix the module boundaries before paying the network tax; most "we need microservices" problems are really "we need clean modules".