Monolith, microservices or serverless?
| Dimension | Monoliths | Microservices | Serverless architecture |
|---|---|---|---|
| In one line | One application, deployed as one unit | Many small services, each deployed on its own and owning its data | Managed services plus functions the platform runs only when an event arrives |
| Unit of deployment | The whole app; every change ships everything | One service; teams ship on their own schedules | One function (or a small group); the platform handles servers |
| How it scales | Run more identical copies behind a load balancer; the shared database is the limit | Scale each service separately, only where load is | The platform starts instances per event and scales to zero when idle (Lambda: 1,000 concurrent executions per Region by default, raisable) |
| Calls between parts | Function calls in one process: nanoseconds, no failure modes of their own | Network calls (HTTP, gRPC) or messages: milliseconds, and they can fail or time out | Events, queues and workflow services; every hop is a network call, and some are billed per call |
| Data | One database; one transaction can cover several features | A database per service; cross-service changes need sagas and eventual consistency | Kept in managed services; a function shouldn’t rely on state between invocations |
| Built-in limits | Build and test time grow with the codebase | Latency adds up along chains of synchronous calls | Lambda runs at most 15 minutes per invocation; cold starts hit under 1% of invocations and take from under 100 ms to over a second |
| Cost shape | A few always-on instances; cheap to run and to understand | Instances per service plus the platform (pipelines, monitoring, tracing) every service needs | Per request and per millisecond; idle costs nothing, but steady high load can cost more than always-on containers |
| Debugging | One process, one log stream, one profiler | Distributed tracing to follow a request across services | Distributed tracing, plus local emulators or deploys to test the wiring |
| Fits | New products, and one to a few teams; modular monoliths stretch much further | Many teams with stable domain boundaries and mature automation | Event-driven glue, spiky or low traffic, scheduled jobs and small APIs |
| Known examples | Basecamp; Shopify’s modular Rails monolith (2.8 million lines in 2020) | Amazon.com (100+ services per page in 2006); Netflix (hundreds of microservices by 2016) | Prime Video’s stream monitor started serverless, then moved to one process and cut cost by over 90% |
Rule of thumb
These are not stages you must pass through. Most systems should start as a well-structured monolith and split off a service only when a team or a part of the load clearly needs its own lifecycle; Fowler’s rule of thumb is not to consider microservices unless the system is too complex to manage as a monolith. Serverless functions fit around either style, for jobs triggered by events. Mixes are normal: a monolith with a few functions for image resizing and webhooks is a common, healthy shape.
Sources
- James Lewis and Martin Fowler: Microservices (Mar 2014)
- Martin Fowler: Microservice Premium (May 2015)
- Martin Fowler: MonolithFirst (Jun 2015)
- Hector Garcia-Molina and Kenneth Salem: Sagas (SIGMOD 1987)
- Lambda docs: Quotas (15 minutes per invocation; 1,000 concurrent executions by default)
- Lambda docs: Execution environment lifecycle (cold starts under 1% of invocations, under 100 ms to over 1 s)
- AWS Lambda pricing (duration rounded up to the nearest 1 ms)
- OpenTelemetry docs: Traces
- DHH: The Majestic Monolith (Feb 2016)
- Shopify Engineering: Under Deconstruction, the State of Shopify’s Monolith (Sep 2020)
- Werner Vogels: A Conversation with Werner Vogels (ACM Queue, Jun 2006) · archived copy, original
- Netflix: Completing the Netflix Cloud Migration (Feb 2016)
- Prime Video Tech: Scaling up the Prime Video audio/video monitoring service and reducing costs by 90% (Mar 2023) · archived copy, original
- Mike Roberts: Serverless Architectures (martinfowler.com, May 2018)