ArchitectureSCALE-SPECIFICCLOUD-SPECIFICGENERAL

Comparing Backend Architectures

Monolith, modular monolith, microservices, serverless and event-driven — when each is useful, what each costs, and how each fails.

What actually happensHow to build it

The requirement, the obvious build, and why it breaks

Every lesson starts where the work starts: someone asked for something, and the first implementation that comes to mind has a problem.

The question

Given a real system, how do you choose between these five, and what makes the choice wrong later?

The requirement

A team has to pick a shape for a system they will run for five years. They want a comparison that does not end with "it depends" and does not end with a winner either.

The obvious build

Pick the modern one. Microservices for anything serious, serverless for anything small, event-driven if there is scale involved. The industry has converged, so follow it.

Why it breaks

There is no convergence. Very large systems run as monoliths, small teams run service graphs badly, and the same technology choice is a success and a failure in two companies with identical traffic.

How it breaks in production
  • There is no convergence. Very large systems run as monoliths, small teams run service graphs badly, and the same technology choice is a success and a failure in two companies with identical traffic.
  • Every one of these is a set of tradeoffs against team count, deploy contention, resource profile and operational capacity. Traffic volume, the thing people quote, is the weakest of the four inputs.
  • Choosing on technology means the costs arrive later and unattributed: the platform team's headcount, the third day of onboarding, the incident where nobody owns the user journey.
  • The choice is not once per system either. Real systems are mixed — a modular monolith with two extracted services, some event-driven paths and a few functions — and that is usually the correct outcome rather than a compromise.
RequirementAPI ContractApplication LogicData AccessExternal DepsConcurrencyFailureSecurityObservabilityDeploymentScale

What is actually happening

  • The five are answers to two independent questions: how many deployables, and how do components talk. Monolith, modular monolith and microservices answer the first; sync versus event-driven answers the second; serverless changes the unit of deployment and scaling (Synchronous vs Asynchronous Communication).
  • That is why they combine. "Event-driven microservices" and "a modular monolith that publishes events" are both coherent; so is "a monolith with three serverless functions for spiky image processing".
  • What actually varies across the rows is what a call costs, what a deploy costs, what a failure reaches, and what you have to operate. Everything else in the comparison follows from those four.
  • Operational complexity is the cost most consistently underestimated, because it is paid by a different team, later, in headcount rather than in a design document (Scoring Operational Complexity in Cloud & Infrastructure).
  • Reversal costs are asymmetric and worth knowing in advance: monolith to modules is a refactor, modules to services is a project, services back to a monolith is a project nobody funds, and serverless back to a service is usually a rewrite of the runtime assumptions.

The five, compared honestly

Read the columns rather than looking for a winner. The one that matters most for a given team is usually operational complexity, because it is a permanent cost paid in headcount, and it is the column that gets left out of design documents.

Note that "when it is useful" is phrased as a condition in every row. If none of the conditions in a row is true of your situation, that row is not a candidate no matter how appealing it looks.

ArchitectureWhen it is usefulStrengthsCostsCharacteristic failureOperational complexity
MonolithOne or two teams; boundaries not yet understood; product still changing shapeReal transactions across the domain; in-process calls; one stack trace; compiler-checked refactoring; one thing to deployOne failure domain; one release cadence; entanglement without discipline; one resource profileCoupling grows until change becomes unpredictable; a leak in one feature takes down all of themLowest: one pipeline, one alert set, one dependency tree
Modular monolithAnything from one team upward that expects to grow; the strongest default for most new systemsEverything the monolith gives, plus enforced boundaries, per-module ownership and a clean extraction path laterDiscipline must be enforced by the build; some indirection; still one failure domain and one cadenceBoundaries erode quietly when the CI rule is a warning; shared/ becomes the real couplingLowest, plus a boundary rule in CI
MicroservicesMeasurable deploy contention across teams; a genuinely divergent resource profile; an isolation or compliance boundary; a forced runtimeIndependent deployment; independent failure and scaling; per-team ownership; technology choice per servicePartial failure at every call; no cross-service transaction; version skew; distributed debugging; operations multiplied per serviceThe distributed monolith: services that must deploy together — every cost, no benefitHighest: pipeline, alerts, dashboards, secrets, ownership and on-call per service (Microservices)
ServerlessSpiky or idle-heavy workloads; short, stateless, independent units; glue and event handlers; teams with no platform capacityNo servers to operate; scales to zero and up automatically; pay-per-use suits bursty work; fast to startCold starts; execution time and memory limits; connection management against relational databases; local development and observability are harder; provider couplingConnection exhaustion under concurrency, and a bill that grows superlinearly with sustained trafficLow per function, high in aggregate: many small units, IAM per function, distributed tracing still required (Serverless Backends)
Event-drivenMultiple or unknown consumers of a state change; workloads needing burst absorption; audit or replay requirementsProducers and consumers decoupled in availability and rate; new consumers added without touching producers; natural fan-outEventual consistency; no ordering guarantee without partitioning; duplicates are normal; a broker and dead-letter path to operate; schema evolution with no compilerBacklogs and stale state with no error anywhere; consumers that were never idempotent producing duplicate effectsMedium to high: broker, dead-letter queues, replay tooling, consumer lag alerting (Event-Driven Backends)

Choosing on constraints, not on preference

SCALE-SPECIFICThe first constraint cannot exist below about three teams. The middle three can bind at any size, and each justifies exactly one deviation from the default rather than a system-wide restructuring.

Convert the discussion from styles to constraints and it usually resolves quickly. Each option below is a constraint that genuinely selects a shape; if none of them is true, the default at the bottom is the answer.

The last option is not a joke. Most systems at most points in their life do not have any of the constraints above it, and the correct engineering decision is to keep every option open at the lowest cost.

What shape does this system need?

Which constraint is actually binding right now?

Several teams blocked on one deploy pipeline

when You can measure the delay per week in hours.

cost Extract along existing module boundaries, one at a time. Full distributed cost per extraction (Microservices).

One component with a wildly different resource profile

when GPU, very large memory, or a scaling curve unlike everything else.

cost One extracted service. Usually the cheapest justified split, and it stops there.

An isolation or compliance boundary

when Regulatory scope, tenant isolation, or a critical path that must survive the rest failing.

cost Duplicate infrastructure and a boundary you cannot relax later.

Spiky, short, stateless work with long idle periods

when Image processing, webhook receipt, scheduled glue, unpredictable bursts.

cost Serverless: cold starts, execution limits, and a connection story you must design (Serverless Backends).

Multiple or unknown consumers of a state change

when Three teams want to react to OrderPlaced and you do not want to know who.

cost Event-driven path: eventual consistency, idempotent consumers, a broker to operate. Orthogonal to how many deployables you have.

None of the above

when Most systems, most of the time, especially new ones.

cost Modular monolith. Cheapest to run, cheapest to change, and it preserves every option above (The Modular Monolith).

What real systems look like

Mature systems are hybrids, and the hybrid is deliberate rather than a failure to commit. A substantial modular core keeps the transactional heart of the domain in one process; two or three services exist because a specific force required them; events connect domains that do not need answers from each other; functions handle spiky edges.

The valuable artefact is not the diagram — it is the written rule for what goes where. Without it, placement is decided per feature by whoever picks it up, and in three years the shape is an accident nobody chose.

one transaction across the core domainsync, bounded, circuit-brokenoutbox, after commitClientsGateway: authn, rate limitModular core: orders, billing, catalogPrimary databaseExtracted: ranking (GPU, own profile)Event logSearch indexerFunction: thumbnailing (spiky)
UserLLMAgentToolDataDecisionHumanGuardrail

How to build it

Most important first.

  • Start from the constraints, in this order: team count and deploy contention, resource-profile divergence, isolation and compliance requirements, and the operational capacity you can actually staff. Traffic is a distant fifth (Microservices).
  • Default to a modular monolith for a new system with one or two teams. It preserves every option, costs least to change, and is the only structure on the list you cannot regret (The Modular Monolith).
  • Add asynchronous paths where the caller does not need the answer, regardless of which deployment shape you chose. That decision is orthogonal and usually more valuable (Background Jobs).
  • Extract a service when a specific force requires independent deployment or independent failure — one service, one named force, along a boundary that already exists.
  • Use serverless where the workload is genuinely spiky, stateless, short and independent, and where cold starts and connection limits are acceptable. Check the relational-database connection story before committing (Serverless Backends).
  • Adopt event-driven paths where consumers are genuinely unknown or plural, and accept that you have taken on ordering, duplication and schema-evolution problems (Event-Driven Backends, Naming Events).
  • Write down the force behind each deviation from the default. In two years that note is the only thing that will tell anyone whether the reason still holds.

What can go wrong

Failure modes
  • Microservices without the platform: no tracing, no per-service ownership, no contract tests — all the distributed costs and none of the tooling that makes them survivable (Tracing From the Backend's Side, Contract Tests Between Services).
  • Serverless with a relational database and no connection management: one connection per concurrent invocation exhausts the database at a level of traffic the equivalent service handled easily (Connection Pools).
  • Event-driven everything: no synchronous path anywhere, so no operation can return a result and every user-visible state is eventually consistent (Eventual Consistency in Practice).
  • A modular monolith with unenforced modules, which is a monolith with extra directories.
  • Splitting on the current org chart, then reorganising, leaving service boundaries that match nobody's ownership.
  • A mixed architecture with no stated rule for what goes where, so the next feature's placement is decided by whoever picks it up.
What can race
  • Every style that crosses a process boundary loses the shared transaction, so concurrent updates to related state need explicit ordering — a version, a sequence or a single writer (Optimistic Concurrency).
  • Event-driven paths race by construction: duplicate and out-of-order delivery are normal, and consumers must be idempotent regardless of which deployment shape surrounds them (Job Idempotency).
Security
  • Fewer deployables means a smaller attack surface and a bigger blast radius; more deployables invert both. Neither is safer in general, and the difference is what your containment controls look like (Defence in Depth).
  • Every distributed style requires that the internal network not be a trust boundary — service-to-service authentication and per-object authorization at every hop (The Trust Boundary).
  • Serverless shifts patching and runtime maintenance to the provider and shifts risk into configuration: over-broad function roles and public endpoints are the common failures (Least Privilege in Infrastructure in Cloud & Infrastructure).
  • Event-driven systems need authorization context carried in the message and validated by the consumer; a message is not authorized just because it is on your broker (Authorization in Backends).
  • Every additional deployable is another dependency tree, another image and another set of credentials to rotate (Dependency Security).
Misreads
  • "Microservices are the mature choice." They are one point on a spectrum with a specific cost profile. Maturity is having a reason, and being able to state it (Microservices).
  • "Serverless is cheaper." It is cheaper for spiky, idle-heavy, short workloads. For sustained throughput it is frequently more expensive than reserved capacity, and it adds cold starts, execution limits and connection-management problems (Serverless Backends).
  • "Event-driven scales better." It decouples availability and absorbs bursts. Sustained throughput is set by consumers and their databases, exactly as in the synchronous version (Synchronous vs Asynchronous Communication).
  • "Pick one and be consistent." Real systems are mixed on purpose. Consistency matters within a boundary, not across a company.
  • "We can migrate later." Some of these migrations are cheap and some are effectively rewrites. Know which direction you are choosing before you need to reverse it.
  • "The architecture determines whether we can scale." The database and the shared bounded resources determine that far more often than the deployment topology does (Failure Propagation).

Operating it

How you see it in production
  • The metric that distinguishes a real service split from a distributed monolith: how many deployables had to ship together for the last twenty changes. Nothing else answers it.
  • Per-component latency and error rates attributed to an owner. In a monolith that requires module tagging; in a service graph it requires tracing. Both are work you must do deliberately.
  • Cost per request or per service, so operational complexity has a number attached rather than a feeling (Cost per Service and the Attribution Problem in Cloud & Infrastructure).
  • Deploy frequency and lead time per team. These are the numbers that tell you whether the shape is currently helping or costing, and they are the ones nobody instruments.
  • For serverless specifically: cold-start rate, concurrency ceiling and downstream connection counts, none of which appear in a normal service dashboard (Startup Time & Cold Start in Cloud & Infrastructure).
What changes at 10x and 100x
  • At 10x traffic, none of these five is disqualified. Stateless instances scale horizontally in every one of them; the bottleneck is normally the database, which the architecture choice does not change (Read Replicas From the Application, Horizontal vs Vertical Scaling).
  • At 10x team size the ranking changes materially: enforced boundaries and independent deployment go from luxuries to the constraint that decides delivery speed.
  • At 100x, hybrids dominate. Large systems are a core monolith or a few substantial services, a set of extracted specialised services, event-driven paths between domains, and functions at the edges. Purity is not observed in the wild at that scale.
  • Serverless economics invert with sustained load: excellent for spiky and idle-heavy workloads, and typically more expensive than reserved capacity for steady high throughput. Where that crossover sits depends on the provider, the memory setting and the workload — measure it rather than assuming a direction (Serverless Trade-offs in Cloud & Infrastructure).
What this costs
  • Every row in the matrix trades operational complexity against independence. There is no option that gives independence for free, and the invoice for the operational half arrives monthly.
  • The default recommended here — modular monolith, extract on a named force — is slower to reach a "modern" architecture and much cheaper to correct. It is a bet that you do not know your boundaries yet, which is almost always true at the start.
  • A mixed architecture is usually right and is harder to explain, onboard into and reason about than a pure one. Write the placement rule down.
  • Choosing for what you expect at 100x usually means paying those costs for years while operating at 1x, and being wrong about the boundaries anyway.

Where this applies

Backend advice is context-sensitive. These labels say what each claim is specific to, and where a different stack or scale would differ.

  • SCALE-SPECIFICEvery row below is scored against a mid-sized system with one to five teams. At one team the operational-complexity column dominates everything else and the modular monolith wins on every criterion. Above roughly ten teams the independent-deployment column starts to dominate and the ranking inverts. The technology does not change; the weights do.
  • CLOUD-SPECIFICThe serverless row depends heavily on the provider: execution time limits, cold-start behaviour, concurrency models and connection handling for relational databases differ enough that a design that works on one platform can be unworkable on another. Managed proxies and pooling services exist and are not equivalent across providers (§99).
  • GENERALThe four underlying axes — cost of a call, cost of a deploy, reach of a failure, weight of operations — are technology-independent and are what the matrix is really comparing.

Where the depth lives

This domain teaches the application-side mechanism and hands the rest off.

Domains that do not exist yet
  • System Design — the same comparison as an interview artefact, where the strongest answer names the binding constraint before naming a topology.
  • Distributed Systems — the guarantees you give up in the last three rows, stated properly.