Compare a modular monolith with microservices across scalability, deployability, testability, operational cost and data consistency. When would you pick each?
answer
- monolith: one artifact, ACID, fast tests
- services: independent deploy + scale, pay in ops
- distributed monolith = worst of both
- sagas / outbox / idempotency replace transactions
- split on a driver, not on principle
basics
~20 sA modular monolith is one deployable unit with internal module boundaries: simple, cheap, easy to test, with single-database transactions, but it scales and ships as a whole. Microservices deploy and scale independently and isolate failures, but add network calls, eventual consistency and operational cost.
solid answer
~50 sScalability: a monolith scales by cloning the whole process, so the heaviest component sets the cost for everything; microservices scale each service to its own load. Deployability: a monolith is one release with one blast radius — any change re-tests and re-deploys everything; services release independently but need versioned, backward-compatible contracts. Testability: a monolith supports fast in-process integration tests; distributed systems need contract tests, service virtualisation and staged environments, and true end-to-end tests get slow and flaky. Consistency: a monolith gets ACID transactions across modules; services need sagas, the transactional outbox and idempotent handlers, and must tolerate partial failure. Cost: microservices demand CI/CD per service, service discovery, distributed tracing and an on-call model. Pick a modular monolith by default — especially when domain boundaries are still uncertain, the team is small, and time-to-market dominates — and extract services where a specific driver demands it: divergent scaling, divergent release cadence, fault isolation, regulatory separation, or independent team ownership.
go deeper
Give the core contrast: one deployable versus many. Name one concrete pro and con on each side, for example that a monolith is simpler to run while microservices can be deployed and scaled separately.
Walk the dimensions explicitly — scalability, deployability, testability, consistency, cost — and name the mechanisms that replace transactions across services: sagas, transactional outbox, idempotency.
Argue from drivers: split only when a specific characteristic demands it, describe the distributed-monolith anti-pattern, and explain incremental extraction along modular seams.
Add the organisational and economic layer: platform investment required before splitting, on-call and ownership models, cognitive load per team, and the reversibility of each option.
### The two shapes A **modular monolith** is a single deployable artifact whose internals are partitioned by business capability (orders, billing, catalog) rather than by technical layer, with boundaries enforced by tooling — module systems, dependency rules, or architecture tests that fail the build when a module reaches into another's internals. Calls between modules are in-process calls through explicit public interfaces. **Microservices** split those capabilities into separately deployed processes, each owning its own datastore, communicating over the network (HTTP, gRPC or messaging). ### Dimension by dimension **Scalability and elasticity.** The monolith scales as a unit: to give the report generator more CPU you replicate everything, including modules that were idle. That is wasteful but operationally trivial, and vertical scaling plus a few replicas covers a very large range of real workloads. Microservices let you run forty instances of checkout and two of admin, which matters when load profiles genuinely diverge by an order of magnitude. **Deployability.** Monolith: one pipeline, one artifact, one rollback — but every change, however small, re-deploys the whole system, so release cadence falls to the slowest common denominator and the blast radius of a bad deploy is total. Microservices: independent release trains and a small blast radius, but only if contracts are versioned and backward compatible. Services that must be deployed together in a fixed order are a *distributed monolith* — all the cost, none of the benefit. **Testability.** In-process, you can start the whole application in a test and assert real behaviour in seconds. Across a network you lose that: you rely on unit tests, consumer-driven contract tests to pin the interfaces, and a thin layer of end-to-end tests that are slow, environment-hungry and flaky. Testability usually *decreases* with distribution. **Data consistency.** A monolith can commit changes across modules in one ACID transaction. Once data is split per service, cross-service consistency needs sagas (a sequence of local transactions with compensating actions), the transactional outbox to publish events atomically with the state change, and idempotent consumers because messages arrive at least once. This is permanent complexity, not a one-off migration cost. **Fault isolation.** In a monolith a memory leak or runaway thread in one module degrades everything. Services isolate failures — but only with bulkheads, timeouts, retries with backoff and circuit breakers; without those, a slow dependency propagates and you get cascading failure, which is *worse* than the monolith case. **Cost and cognitive load.** Microservices need per-service pipelines, artifact registries, service discovery, centralised logging, distributed tracing, dashboards and an on-call rotation. That platform work is a permanent tax paid by everyone. ### Choosing Default to the modular monolith when the domain is not yet well understood, the team is small, and getting to market matters most — boundaries are cheap to move inside one process and brutally expensive to move across a network. Split when a *specific* driver demands it: a component whose scaling profile differs by an order of magnitude, a part that must release far more often, something that must fail independently, a regulated slice needing physical isolation, or an organisational need for autonomous team ownership. Extract incrementally, strangler-fig style, along the seams the modular monolith already gave you.
- What is a 'distributed monolith' and how do you recognise one?Services that are physically separate but must be released together, share a database, or break when any one is redeployed. Signs: lock-step versioning, a shared schema, end-to-end tests as the only safety net, and one team changing many services at once. It pays the full distribution cost while keeping monolithic coupling.
- If you keep a modular monolith, what do you do now to keep future extraction cheap?Partition by business capability rather than technical layer, expose each module only through an explicit interface, forbid cross-module database access (schema or table ownership per module), enforce the rules with automated architecture tests, and prefer asynchronous, message-shaped interactions where you expect a future split.
A shared house versus separate flats: the house is cheaper and settling arguments is easy because everyone is in one room; separate flats let each tenant renovate without permission, but now you need post, keys, utility contracts and a plan to have dinner together.
saying these in an interview costs you the question
- Claiming microservices are inherently more scalable — both scale; they differ in scaling granularity and cost
- Saying microservices improve testability; distribution generally makes integration testing harder
- Ignoring that splitting the database is the real cost, not splitting the code
- Treating 'monolith' as a synonym for 'big ball of mud' — modularity is orthogonal to deployment shape
- Splitting by technical layer (a 'UI service', a 'database service') instead of by business capability