Serverless: functions, cold starts and pay-per-use
Sunday, 19:20: hotels upload about 4,000 photos in two hours to prepare the week, the web servers spend their CPU resizing them, and search p95 jumps from 0.4 to 2.6 seconds.
Hand the cloud your code and let it run only when something happens. Great for quiet or spiky work, pricey for busy, steady work.
- Problem
Traffic is spiky or tiny: a webhook fires 200 times a day, an image resize runs after uploads, a marketing page gets 50x traffic during a campaign.
- Why the simple system fails
Always-on servers are paid 24/7 even when idle, must be sized for the peak, and need patching, scaling and monitoring by your team.
- Engineering concept
Run code only when an event arrives. The platform creates instances on demand, scales them out per request and back to zero, and bills per invocation and execution time.
- Technology
FaaS (Function as a Service) platforms, event triggers (HTTP, queues, object storage, schedules), managed databases and queues around them.
- Trade-offs
No servers to run and near-zero idle cost, versus cold starts, execution limits, cost at steady high volume, harder local testing and vendor lock-in.
The problem
2 min- Omar · On-call SRESearch p95 is 2.6 s (normally 0.4). Web servers at 95% CPU. About 4,000 photos uploaded since 19:00.
- Karim · CTOTwo more servers just for Sunday? Or is this a job for serverless?
- Lina · Senior engineer, your mentorYou lead this one. Commit to a guess.
The big idea
4 minmeans paying per use instead of for capacity: perfect for bursty work nobody waits on, costly and slow for steady traffic guests wait on.
Always-on servers are a company car fleet: you pay for every car, busy or not. Functions are taxis: ten appear when ten people need them, and you pay per ride. A team commuting the same route all day is cheaper with its own cars.
How it works
5 minWith , you deploy code plus a trigger (an HTTP request, a file landing, a queue message, a schedule); the provider runs, scales and patches the servers. Photos arrive at about 13 an hour all week, then 2,000 an hour on Sunday evening: servers sized for Sunday are idle 98% of the time.
- 1Ask for an upload URL
The dashboard gets a short-lived signed URL from the monolith, so the big file never passes through our servers.
- 2Upload straight to storage
The browser sends the photo directly to , which emits an
object-createdevent. - 3One function per photo
The platform runs
resizePhotoper event: 4,000 photos can mean hundreds of instances at once. - 4Save results, keep nothing
Thumbnails go to storage, metadata to the database. The instance keeps no state; it may vanish any time.
- 5Back to zero
After the burst, instances shut down: , no events, no cost. The next photo pays a : the delay while the platform starts a fresh instance.
Watch a decision being made
4 minYou move resizing to a function (Lina checks the numbers)
Stop Sunday uploads from slowing search, without paying for idle capacity all week and without endangering the shared database.
- 1Measure the shape, do the cost math
~24,000 photos a month, ~1.5 s and 1 GB each, nobody waiting. 24,000 × 1.5 × 1 = 36,000 GB-seconds ≈ $0.60 a month at an illustrative price. Two extra Sunday servers: about $120.
Why: "Serverless is cheaper" is only true for some shapes. Ten minutes of arithmetic turns a debate into a number.
- 2Design the trigger path, keep the handler thin
Signed URL → direct upload to storage →
object-createdtriggersresizePhoto. The resize logic stays in the plainphotosmodule; the handler only reads the event and saves the result.Why: Big bytes never touch our servers, and provider lock-in stays in the thin glue.
- 3Protect the shared database
One connection per instance, created outside the handler, through the pooler; a maximum concurrency of 50 for this function.
Why: Functions scale out in seconds; PostgreSQL's 200 connections don't, and they're shared with every booking. With a cap, a burst queues a little instead.
- 4Make retries safe
Output files are named by the photo's key and the update is
SET ready = true, so running an event twice gives the same result.Why: Event triggers deliver at least once. Idempotent work turns duplicates into a non-event.
In real life
3 miniRobot: a serverless cloud backend
- Has publicly described running its robot cloud largely on serverless services
- Traffic is very uneven through the day; a small team avoided running servers
Fits uneven, event-shaped traffic and a small ops team.
37signals: leaving the cloud for its own servers
- Publicly described moving HEY and Basecamp to servers it owns
- Large, steady traffic; reported big savings, taking on hardware work
Fits steady, predictable load where idle capacity is rare.
What goes wrong
3 minInstances go cold, so real guests hit seconds of p99.
Measure cold starts, or keep the endpoint always-on.
At steady high volume, per-call billing can cost several times more.
Model cost at expected and 10× volume first.
Execution limits kill it halfway, after side effects.
Idempotent steps through a queue, or a container job.
Trade-offs
3 min| Option | Fits when | You pay |
|---|---|---|
| Functions | Spiky, low or event-driven work; tiny ops team | Cold starts, time limits, cost at steady load, lock-in |
| Always-on servers | Steady traffic, strict p99, long connections | Idle capacity; you patch and scale |
| Hybrid | A steady core plus bursty side jobs | Two deployment models |
- Karim · CTOSixty cents a month. Let's move the whole booking API to functions and stop paying for four servers.
- Lina · Senior engineer, your mentorIt serves about 40 requests a second all day. Steady load costs more per call, and cold starts would land on checkout.
- Karim · CTONo servers to patch, no capacity planning. Engineer time is our most expensive resource.
Your job as tech lead
4 minClaude Code can write the code for this. You still own the decisions, and you have to know what good looks like to accept or reject its work.
- Which workloads go to functions, by traffic shape: Sunday photo bursts yes; the steady booking API with 0.3–1.5 s cold starts, no.
- The cost comparison with your numbers (~$0.60 vs ~$120 a month), as arithmetic, not a claim.
- The concurrency and connection budget: 200 connections, ~100 used, so the function gets a cap of 50 and the pooler.
On Sunday evenings hotels upload ~4,000 photos and resizing on our web servers pushes search p95 from 400 ms to 2.6 s. Plan a move to a serverless function; no code until I approve. Signed upload URL from the monolith, direct upload to storage, object-created triggers resizePhoto. Resize logic stays in the photos module; thin handler. One pooled connection per instance, created outside the handler; max concurrency 50. Reruns must be idempotent. Show the monthly cost formula. Don't touch the booking API.
- The cost math: invocations × seconds × GB = GB-seconds, × price. 24,000 × 1.5 × 1 = 36,000.
- A concurrency limit in the infrastructure file, and the connection created outside the handler through the pooler.
- A test runs the handler twice on the same event: same files, same row.
- A new database connection inside the handler on every invocation.
- Moving the steady booking API to functions "for scaling" without cold-start or cost numbers.
- Business logic written inside the provider-specific handler.
Check yourself
2 minNext episode: a new engineer asks why StayHub uses an outbox, ports for payments and a photo function, and the only answers are in old chat threads.
Mark it complete to update your skill map.
Go deeper (optional)You don't need this to understand the lesson. Open it when a project needs the details: Technical deep dive, Visual diagram, Analogy, Why it matters, Check your understanding, Practice ladder, Explain it yourself.
Technical deep dive
You lead; Lina only checks the numbers. Here's how you'd move resizing off the web servers.
Moving photo resizing to a function
Stop Sunday photo uploads from slowing search, without paying for idle capacity all week and without endangering the shared database.
- 1Measure the workload's shape
~4,000 photos Sunday 19:00–21:00, ~300 a day otherwise: about 24,000 a month. Each resize: ~1.5 s, 1 GB. Nobody waits for the result in real time.
Why: Serverless wins on bursty, asynchronous work and loses on steady, latency-critical work. The shape decides before anything else.
- 2Do the cost math
24,000 × 1.5 s × 1 GB = 36,000 GB-seconds. At an illustrative $0.0000167 per GB-second, about $0.60 a month, plus about half a cent in request fees. Two extra Sunday servers: about $120 a month.
Why: "Serverless is cheaper" is only true for some shapes. Ten minutes of arithmetic turns a debate into a number.
- 3Design the trigger path
Browser asks the monolith for a signed upload URL → uploads directly to object storage → each
object-createdevent triggersresizePhoto.Why: The large bytes never touch our servers or the function's request path, which keeps both cheap and fast.
- 4Keep the handler a thin adapter
Resize logic lives in the plain
photosmodule; the handler only reads the event, calls the module and saves the result.Why: Same idea as last episode's ports: the business logic stays testable and portable, and lock-in stays in the thin glue.
- 5Protect the shared database
One connection per instance, created outside the handler, through a pooler; reserve a maximum concurrency of 50 for this function so a burst queues instead of opening 400 connections.
Why: Functions scale out in seconds; PostgreSQL's 200 connections don't. The database is shared with every guest's booking.
- 6Make retries safe
The platform may run an event twice. Output files are named by the photo's key and the update is
SET ready = true, so running twice gives the same result.Why: Event triggers deliver at least once. Idempotent work turns duplicates into a non-event.
| Workload | Function math (512 MB, 100 ms) | Function cost / month | Always-on alternative |
|---|---|---|---|
| Webhooks: 10k calls/day | 300k calls × 0.05 GB-s = 15k GB-s | ≈ $0.30 | ≈ $30 for one small container, idle 99.9% of the time |
| API: 50 req/s steady | 130M calls × 0.05 GB-s = 6.5M GB-s | ≈ $135 | ≈ $60–90 for 2–3 containers with headroom |
| API: 500 req/s steady | 1.3B calls → 65M GB-s | ≈ $1,350 | ≈ $300–600 for a container fleet |
| Concern | Typical behaviour | Mitigation |
|---|---|---|
| Cold start | Tens of ms to several seconds | Smaller bundles, init outside the handler, provisioned concurrency for critical paths |
| Max execution time | Minutes, not hours, per call | Split work into steps, or use a container job |
| State | Instances are short-lived; memory may vanish anytime | Keep state in a database, cache, storage or queue |
| DB connections | Each instance may open its own | Pooler, 1 connection per instance, concurrency cap, queue |
Visual diagram
A serverless photo pipeline for a hotel platform. Every compute box runs only when an event arrives.
Analogy
- Getting around townOwning a company car fleetRunning codeAlways-on servers
- Getting around townCalling a taxi, paying per rideRunning codeServerless functions
- Getting around townTen taxis for ten peopleRunning codeOne copy per request
- Getting around townWaiting for a taxi to arriveRunning codeA cold start
- Getting around townLoyalty points that don't transferRunning codeVendor lock-in
Where the analogy breaks: a taxi never forgets you mid-ride. A function copy can vanish between calls, so it must not keep anything important in memory.
Why it matters
Lots of work is quiet most of the time. A payment gets a few hundred calls a day. Photo thumbnails come in bursts on Sunday evenings. A nightly report runs for three minutes.
With your own servers, you either pay for the peak all month, or you fall over at the peak. And someone has to patch the machines and tune the scaling.
With functions, scaling and patching are the provider's job, and idle time is free.
Spiky or quiet
- Webhooks, thumbnails, nightly jobs
- Idle time costs nothing
- Bursts handled for you
Serverless shines
Busy all day
- Constant high traffic
- Per-call price adds up
- Cold starts on urgent paths
Always-on servers are cheaper
Check your understanding
Practice ladder
Trace warm and cold instances
Scenario Warm-upStayHub's createUploadUrl function (called when a hotel starts an upload) has: - Cold start: 800 ms; handler: 30 ms - An idle instance is shut down after 10 minutes without requests - One instance handles one request at a time
Requests arrive at: 09:00:00, 09:00:05, 09:00:05 (two at once), 09:14:00.
For each request, say whether it hits a warm or cold instance, and its latency. How many instances exist at 09:00:06?
Finish the cost comparison
Estimation PracticeLina's cost note for moving the payment-provider webhook handler to a function has two blanks. Use illustrative prices: $0.20 per million invocations and $0.0000167 per GB-second.
- Webhooks: 6,000 per day (≈ 180,000 per month), 200 ms each, 512 MB.
- Today they run on one always-on small server that does nothing else: $30/month.
- GB-seconds per month: ___
- Function cost per month (compute + requests): ___
- Decision and one risk to check.
Nightly partner reports
Design PracticeEvery night at 02:00, StayHub must generate a PDF revenue report for each of the 1,800 partner hotels and email it. Each report takes ~4 s and ~300 MB. Today one worker process does them one by one: it takes 2 hours and sometimes isn't done when hotels check their email at 06:00 after a slow night.
Design a serverless version: the trigger, how work is split, what limits you set, how retries stay safe, and the rough monthly compute cost.
Search autocomplete on functions?
Decision StretchThe Search team wants to put city autocomplete ("Par…" → Paris, Parma) on functions, to scale for a TV campaign next month.
Facts: autocomplete gets ~120 requests/s during the day, ~10/s at night; guests expect results in under 100 ms while typing; the campaign may bring 5× traffic for one evening. The handler is 15 ms; cold starts would be 300–900 ms. The monolith's 4 servers currently run at 35% CPU.
Write a decision: "We chose X over Y because…, accepting the risk of…", and say how you'd handle the campaign.
Explain it yourself
Explain to a product manager when you would run a feature on serverless functions and when you would not.