A serverless cloud function is a single deployable unit of backend logic; a microservice is a whole independently operated service. That one sentence is the entire comparison in miniature — everything else (ops burden, cost curves, cold starts, state) follows from what the unit of deployment is: a function you hand to a platform, or a service you run yourself.
Key takeaways
| Question | Answer |
|---|---|
| The core difference | Unit of deployment: one operation (function) vs. one owned capability (service) |
| Who operates it | Functions: the platform. Microservices: your team, per service |
| Idle cost | Functions scale to zero; a service fleet bills around the clock |
| The function tax | Cold starts, execution time limits, statelessness |
| The service tax | Pipelines, orchestration, monitoring, on-call — multiplied per service |
The unit of deployment, in code
Here is the entire deployable for a checkout operation — one function, no repo-per-service, no container image, no pipeline:
// JavaScript / Node.js — the function is the whole deployable
// cloud/main.js — runs on the platform; there is no service to operate
Parse.Cloud.define('checkout', async (request) => {
const cart = await new Parse.Query('Cart').get(request.params.cartId, {
sessionToken: request.user.getSessionToken(),
});
// …price the cart, reserve stock, write the order…
return { orderId: cart.id, status: 'confirmed' };
});
// Any client calls it by name — no gateway, container, or pipeline
const result = await Parse.Cloud.run('checkout', { cartId }); // Flutter / Dart — Back4app Flutter SDK
// Calling the function: one named endpoint, no service discovery
final checkout = ParseCloudFunction('checkout');
final response = await checkout.execute(
parameters: {'cartId': cartId},
);
if (response.success) {
print(response.result['status']); // confirmed
} else {
print(response.error?.message);
} // iOS / Swift — Back4app Swift SDK
// Calling the function: one named endpoint, no service discovery
struct Checkout: ParseCloudable {
typealias ReturnType = [String: String]
var functionName: String = "checkout"
var cartId: String
}
let result = try await Checkout(cartId: cartId).runFunction()
print(result["status"] ?? "") // confirmed // Android / Kotlin — Back4app Android SDK
// Calling the function: one named endpoint, no service discovery
val params = hashMapOf("cartId" to cartId)
ParseCloud.callFunctionInBackground<Map<String, String>>(
"checkout", params
) { result, e ->
if (e == null) {
Log.i("Checkout", result["status"] ?: "")
} else {
Log.w("Checkout", "failed: ${e.code}")
}
} The microservice equivalent of that snippet is a repository: an HTTP server, a container build, deployment manifests, service discovery, health checks, a dashboard, and an on-call rotation — before the first line of checkout logic.
Serverless cloud functions vs. custom microservices
| Dimension | Serverless cloud functions | Custom microservices |
|---|---|---|
| Unit of deployment | A function | A service (process + API + data) |
| Infrastructure you operate | None — platform-managed | Containers, orchestration, networking per service |
| Scaling | Automatic, per invocation, to zero | You configure it; capacity runs even when idle |
| State | Stateless by contract; state lives in the database | Can hold in-memory state (at a price) |
| Latency profile | Warm calls are fast; idle instances pay a cold start | Consistent — the process is always up |
| Runtime | Platform-provided (typically a managed JavaScript runtime) | Anything you can containerize |
| Long-running work | Cut off by execution time limits | Unlimited |
| Team shape | Product engineers only | Platform/DevOps capacity required |
| Cost curve | Per-execution; unbeatable when spiky, crosses over at sustained load | Flat; efficient at steady high volume |
The microservices literature is explicit that the architecture’s benefits are bought with serious operational maturity — automated deployment, sophisticated monitoring, design-for-failure. Functions outsource exactly that bill to the platform, which is why Mike Roberts’ serverless analysis frames FaaS as trading control for radically less ops. Neither trade is free; the question is which tax your team can afford.
What actually happens to a request
Notice what the bottom half adds that the top half cannot express: service-to-service calls, per-service data stores, and a gateway — the coordination surface where microservice complexity actually lives. Distributed transactions, retries, and partial failures between the cart and order services are your code; in the function model, the same workflow is usually one function and one database, and the event-driven pieces (triggers, scheduled jobs) hang off the platform rather than off queues you operate.
When functions replace a microservice fleet — and when they can’t
The honest observation behind the BaaS pattern: most microservices in a typical product are thin. They validate input, enforce a rule, read or write a database, and call a neighbor — the operational shell around that logic is 90% of their weight. Cloud functions inside a BaaS delete the shell: the database, auth, file storage, and APIs are platform services, so each “service” collapses into a handful of functions and triggers. Teams of one to ten shipping CRUD-plus-logic products rarely need more.
The ceiling is equally honest. Functions cannot host a recommendation model that needs specialized hardware, a video transcoder that runs for an hour, a WebSocket fan-out engine with custom tuning, or a component whose sustained throughput makes per-invocation pricing the expensive option. When a component crosses those lines, extract it as a real service and let it interoperate with the functions — extraction of a proven hot spot is a far cheaper migration than decomposing a speculative fleet you built too early, which is the same lesson the monolith-first argument teaches one level up.
Common use cases
- Functions: API and mobile backends. Request-shaped logic over a managed database — the dominant case, and the one BaaS platforms package end to end.
- Functions: triggers and glue. Validate on save, resize on upload, sync to a third-party API, run nightly jobs — short-lived reactions that would embarrass a dedicated service.
- Functions: spiky and unknown traffic. Launches, campaigns, MVPs — scale-to-zero absorbs both the spike and the silence.
- Microservices: sustained heavy components. Search, feeds, pricing engines — steady load where always-on capacity is cheaper and tunable.
- Microservices: special runtimes. Non-standard languages, native dependencies, GPUs, long-running processes.
- The hybrid. Functions for the API surface and events; one or two extracted services for the components with numbers that demand it.
Should you build functions or microservices? A decision matrix
| Your situation | Lean |
|---|---|
| Small team, product logic is mostly CRUD + rules | Functions on a BaaS — the fleet adds cost, not capability |
| Traffic is spiky, low, or unpredictable | Functions — scale-to-zero is the whole argument |
| A component runs minutes-to-hours per job | Microservice (or a background-job system) — timeouts rule functions out |
| Sustained high throughput on a hot path | Microservice — flat-rate capacity wins the cost curve |
| Strict tail-latency budget on every request | Microservice — no cold-start variance |
| Custom runtime, native deps, special hardware | Microservice — platforms run what they run |
| You have no dedicated ops capacity | Functions — the fleet’s tax arrives whether or not you budgeted it |
| One hot spot inside a function-shaped product | Hybrid — extract that one service, keep the rest as functions |
Limitations and trade-offs
- Functions: execution limits are hard walls. Long work must move to job systems or services — no amount of cleverness extends a timeout gracefully.
- Functions: cold starts and chains. Rare per invocation, but function-calling-function architectures stack them; keep hot paths shallow.
- Functions: platform coupling. The runtime and its APIs are the platform’s — an open-source foundation you can self-host is the practical hedge against lock-in.
- Microservices: the ops bill is per service. Pipelines, monitoring, versioned contracts, and on-call multiply with the fleet; underestimating this is the classic failure mode.
- Microservices: distributed-systems problems arrive on day one. Network partitions, partial failures, and cross-service consistency are architectural constants, not edge cases.
- Both: state is externalized anyway. Functions force it; well-run services choose it. The database, not the compute, ends up holding the truth in either design.
Serverless functions and microservices on Back4app
Back4app is an open-source Backend-as-a-Service (BaaS) platform that combines a managed database, auto-generated REST and GraphQL APIs, authentication, file storage, and Cloud Code serverless functions. It is the function side of this comparison delivered whole: Cloud Code runs named functions like checkout above, plus database triggers and scheduled jobs, next to everything a microservice fleet exists to provide — so the fleet most products would have built becomes functions over managed services. And because the foundation is open source and self-hostable, the extraction path stays open: when one component outgrows the function model, it can graduate to a dedicated service without abandoning the platform around it.
Frequently asked questions
Is a serverless function a microservice?
Not quite — the granularity differs by an order of magnitude. A microservice owns a business capability: its own process, data store, deployment pipeline, and API surface. A function owns one operation. A single microservice typically decomposes into many functions, and the platform, not your team, supplies the process, scaling, and runtime around each one.
Can serverless functions replace microservices?
For many products, yes — especially when the services would mostly wrap a database with validation and light workflow. Functions on a BaaS inherit the database, auth, and APIs, leaving only genuine business logic to write. The exceptions are real: long-running work, custom runtimes, sustained heavy throughput, and strict latency floors still favor an operated service.
Which is cheaper: functions or microservices?
At low or spiky volume, functions — you pay per execution and idle costs are zero, while a microservice fleet bills for containers around the clock plus the engineering time to operate them. At sustained heavy volume the curves cross: always-on capacity gets cheaper per request. Count the ops salary in the comparison; it usually dominates the infrastructure line.
Do cold starts make functions slower than microservices?
Only on the fraction of invocations that hit an idle instance — typically milliseconds to about a second, versus an always-warm service's consistent latency. Steady traffic keeps function instances warm, and chained functions are the case to watch, since each hop can add its own cold start. For strict tail-latency budgets, an always-on service still wins.
When do custom microservices clearly win?
Long-running or stateful work that outlives function timeouts, custom runtimes or system dependencies the platform does not offer, specialized hardware, sustained high throughput where always-on capacity is cheaper, and teams that need full control of the network and deployment topology. If several of those apply to a component, that component wants to be a service.
Can serverless functions keep state?
Not between invocations — every function call starts from a clean slate, and anything worth keeping must live in the database or a cache. Microservices can hold in-memory state, at the price of making scaling and failover harder. In practice both architectures converge on the same discipline: externalize state, treat compute as disposable.
Can you combine functions and microservices?
Yes, and mature systems usually do. The pragmatic split: functions handle request/response API logic, database triggers, scheduled jobs, and event glue; dedicated services handle the few components with heavy, steady load or special runtime needs. Starting with functions and extracting a service when the numbers demand it is cheaper than the reverse migration.