Serverless gets recommended as a default and criticised as a trap, and both positions skip the useful question: what shape is your workload? For a small team the honest answer is that serverless is very good at some shapes and actively bad at others.
Where it clearly wins
Work that arrives in bursts, takes an uneven amount of time, and does not need an immediate response is the ideal case. Our warehouse vision pipeline is exactly this: image missions arrive unpredictably, a batch might be twelve images or twelve hundred, and inference takes as long as it takes.
Running that on always-on servers means either paying for capacity you use in bursts, or falling over when a big batch arrives. With Lambda for orchestration and SageMaker asynchronous inference for the heavy work, the pipeline absorbs a large batch without blocking a request and costs nothing while idle.
- Image, document, and video processing
- Scheduled jobs, reminders, and report generation
- Webhook and event handling
- Anything where the response is "we will let you know"
Where it is the wrong tool
Steady, predictable, latency-sensitive traffic is the case where serverless stops making sense. A consistently busy API with a tight latency budget will usually be cheaper and simpler on a container that is always running, and you avoid cold starts entirely.
Long-running stateful work and anything that needs a persistent connection also fit badly. Fighting the execution model is a sign the model is wrong for the job.
The operational work people underestimate
Serverless removes server management, not operational thinking. Distributed work introduces its own problems, and they need deliberate handling rather than discovery in production.
- Idempotency, a retried event must not process twice
- Job locking, two workers must not claim the same task
- Dead-letter queues, failures need somewhere to go and someone to look
- Cleanup, orphaned artefacts and stale locks accumulate
- Cost alarms, a loop is a bill, not an outage
A pragmatic default
For most small teams the sensible architecture is not a choice between the two. Run the predictable request-response part of the product on containers, and push the bursty asynchronous work (processing, jobs, notifications, inference) into serverless functions and queues.
That split gives you predictable latency where users are waiting and elastic capacity where they are not, without committing the whole system to one execution model.