What you will take away
- Serverless is unbeatable for spiky and intermittent workloads.
- At steady high volume, per-request pricing loses to a reserved instance.
- Database connections and cold starts are the two constraints to design around.
What serverless is genuinely good at
Workloads that are spiky, intermittent or event-driven. A function that processes uploaded images, a webhook receiver, a nightly report, a queue consumer whose depth varies by an order of magnitude across the day - these fit the model exactly, scale without configuration, and cost nothing when idle.
The second benefit is operational. There is no fleet to patch, size or monitor for capacity, which for a small team is often worth more than the compute saving.
Where the economics turn
Per-request pricing is excellent at low and variable volume and poor at steady high volume. An endpoint serving constant traffic all day is usually cheaper on a reserved instance, sometimes substantially. The crossover is worth calculating with real numbers before committing an architecture to it.
Serverless bills you for demand. If your demand is flat and permanent, you are paying a premium for elasticity you never use.
The constraints that bite
- Cold starts, which matter for user-facing latency and much less for background work.
- Execution time limits, which rule out long-running jobs unless they are split into steps.
- Database connections - thousands of concurrent function instances will exhaust a traditional pool without a proxy in front of it.
- Local development and debugging, which are harder than running a service on your machine.
- Deeper coupling to one provider than most other choices, because the surrounding services come with it.
The hybrid that usually wins
Most systems we build are not entirely one or the other. The core API with steady traffic runs as a container service; the spiky, asynchronous and scheduled work runs as functions. Each part is on the model that suits its traffic shape.
That also keeps the coupling contained. The business logic stays in code you could run anywhere, and the provider-specific surface is limited to the event plumbing around it.
Practices that keep it manageable
Keep functions small and single-purpose, but resist splitting a coherent workflow into fifteen functions that must be traced together to be understood. Make every handler idempotent - at-least-once delivery means a retry will happen eventually.
Set up tracing before you need it, define dead-letter queues for every consumer, and load-test with production-shaped concurrency. Serverless failures tend to appear only at scale, which is precisely when you can least afford to be learning.