Azure Functions, explained: code that runs only when there is work
Most cloud code spends its life waiting — a server billed by the hour to do something for a few seconds a day. Azure Functions flips that: your code sleeps for free, wakes on an event, does its job, and goes back to sleep.
There is a whole category of work in every system that is a poor fit for an always-on service: resize the image someone just uploaded, run the nightly cleanup, react when a message lands on a queue. Running a full VM or app around the clock for a job that fires occasionally is paying rent on an empty room. Azure Functions is Microsoft's answer — a serverless platform where, in its own framing, you build apps "with less infrastructure and lower costs" and let the cloud "provide all the up-to-date resources needed to keep your applications running." You write the function; Azure handles the servers, the scaling, and the waiting.
The core idea: a trigger wakes your code
Every function starts with a trigger — the single event that causes it to run. No trigger, no execution (and, on a consumption plan, no bill). The triggers map onto the everyday jobs you would otherwise script:
- A blob trigger runs when a file is uploaded or changed in storage.
- A timer trigger runs on a schedule — the serverless cron job.
- An HTTP trigger runs on a web request, so a function becomes a lightweight REST endpoint.
- A queue or event trigger runs when a message arrives on Queue Storage, Service Bus, or Event Hubs.
Your code does not poll for these; the platform wakes it when the event happens. That inversion — the event calls the code, not the other way round — is the whole event-driven model.
Bindings: connections without the plumbing
Alongside triggers, Functions gives you bindings — declarative connections to other services so you skip the boilerplate. Microsoft describes them as a way to "connect your functions to other services without having to write extra code." An input binding hands your function data (a row, a blob) when it starts; an output binding writes your result somewhere (a queue, a database) when it finishes. You declare "this function reads from that blob and writes to this queue," and the SDK wires up the clients, credentials, and serialization for you. Less glue code, fewer places for a connection string to leak.
A server waits, on the clock. A function sleeps, for free, and wakes only when there is something to do.
On a consumption-style plan, Functions scales with demand and all the way down to zero, so idle costs nothing. The trade is a cold start: a function that has been asleep takes a moment to spin up on its first call. For steady, latency-sensitive traffic, the Premium plan keeps instances warm; for spiky, occasional work, scale-to-zero is exactly what you want.
Where it runs
Functions offers a few hosting plans, and knowing they exist is enough to start:
- Flex Consumption — the recommended default: fast event-driven scaling, virtual-network integration, and pay-as-you-go billing.
- Premium — always-warm instances for the fastest response and longer-running work.
- Dedicated — run inside an existing App Service plan when you want predictable, flat costs.
- Container Apps — run containerized functions alongside your microservices.
When to reach for it
Functions is the right tool when work is event-driven and intermittent: glue between services, scheduled maintenance, a webhook handler, light data processing, a small API that does not justify a full app. It is the wrong tool for long-running, always-busy workloads or anything needing steady sub-second latency without warm instances — that is what App Service and Container Apps are for. The instinct to grow is: the third time you find yourself writing a script to "run this when that happens," stop and make it a function. You will have deleted a server, a schedule, and a chunk of glue code in one move — which is exactly the kind of quiet subtraction that makes a cloud engineer look effortless.