Serverless and Edge Functions: where the classic backend breaks
The biggest bottleneck in request processing shows up not on an average day, but at peak. A classic server backend is sized for peak load: the pool of workers, connections, and memory is kept with headroom for rare spikes, and the rest of the time that headroom sits idle. As soon as traffic jumps severalfold, free handlers run out — overload begins at peaks.
Then a cumulative effect kicks in: the request queue grows, response degradation cascades — response time goes from fractions of a second to seconds, the 95th percentile spikes, and some requests time out before even entering business logic.
The user sees not "slow" but "not working," and leaves. For the service owner, these are lost orders, not just a line in the logs, and they start looking into it only after the incident.
Horizontal autoscaling covers this only partially: a new instance takes minutes to come up, warms the cache, and opens connections to a database that has its own limit — by scaling the application, we hit the connection pool and shared locks. The peak passes before the infrastructure reacts to it.
Serverless and Edge Functions remove exactly this limitation: a handler starts per request and scales with actual load, without warm-up or a permanent reserve. Below we break down how this is solved in practice — and where a classic backend remains the right choice.
What edge request processing provides
Edge processing moves logic execution closer to the visitor: the handler runs at a network node near them, not in a single data center. Below are four effects that are visible in metrics, not in wording.
Response time on distributed requests
A significant part of response time is not computation, but network round trips between the visitor and the server.
When a function executes at the nearest node, each round trip is shortened, and across a chain of several calls this adds up to a noticeable difference for the user.
Independent latency measurements between regions are published by M-Lab — their open measurements are convenient to use as a reference point when choosing where to place handlers.
Eliminated failure classes under peak load
Incoming request queue, database connection pool exhaustion, complete site failure — with an edge architecture, these failure classes cease to be systemic.
When an individual node degrades, traffic moves to a neighboring one, and downtime is measured in seconds, not in the time of manual write switching.
The platform handles scaling for peaks: handler instances are added according to actual traffic and removed when it subsides. A permanent reserve for the worst day of the week is no longer needed — it turns into quota headroom.
Dedicated server, Serverless, and edge functions: comparison
The execution model defines three things: how the application survives peak load, how long it takes to release a change, and which failures are even possible.
A dedicated server is predictable but sits idle during lulls; edge functions respond quickly but live in a stripped-down environment. The choice here is not about "what is more modern," but about what hits a wall in your case.
Below are the alternatives that are actually chosen between at the design stage, and their limitations.
| Option | When it fits | Limitations |
|---|---|---|
| Dedicated server | Steady traffic, long-lived connections, background tasks | Idle during lulls, manual scaling, updates are on you |
| Container | Need control over the environment without being tied to hardware | Cold start, requires orchestrator and limits |
| Serverless functions | Bursty load, webhooks, event processing | Execution time limit, no persistent state |
| Edge functions | Personalization, routing, response closer to the user | Stripped-down environment, memory limits, no heavy libraries |
| Hybrid architecture | Storefront on functions, core and queues on the server | Harder debugging and tracing, need unified release rules |
When a hybrid architecture is beneficial
A hybrid architecture makes sense when an application has two different load profiles: response to user action and background processing.
We assign the storefront, authorization, and routing to edge functions, events and queues to serverless functions, while keeping the database, search, and heavy computation on a dedicated server or in containers.
The boundary runs along state: the closer a function is to the data, the smaller the benefit of moving it out.
How we deploy backend logic: stages of work
Deploying backend logic starts not with code, but with an inventory of what is already running. We capture a load profile on real traffic: which routes are called most often, where the database connection is held, which operations hit a cold start.
Only after that do we decide what goes into managed functions and what stays on persistent processes.
Audit and architecture
- Backend audit and load profile — a map of handlers, peak requests, response duration, share of repeat requests. The numbers show where a function pays for itself and where it will add latency.
- Deployment architecture — we distribute routes across execution zones, record failure points and application behavior when an external service is unavailable.
- Environment and database setup — separate environments, migration order, access and secrets outside the repository, execution time limits for each function.
Setup, verification, handover
- Logic migration and storage connection — step by step and with backward compatibility: the old and new environments run in parallel, traffic is switched in increments.
- Load and failure testing — a run on the profile captured in the first stage, plus disabling the database and external services. We see how the queue recovers and whether events are lost.
- Commissioning and handover — we enable observability, alerts for errors and latency, and hand over the architecture, configurations, and rollback procedure.
What remains in your repository after handover
After launch, the project remains with you not as a "black box," but as a working repository that your team can read and reproduce.
This also applies to function-based applications (Serverless and Edge Functions): we hand over not a set of files "as they turned out," but a description of the environment, connections, and operating rules.
Below is the mandatory minimum that is handed over to you at completion:
- Environment configurations — development, staging, production: the same code deploys predictably, with differences only in parameters.
- Request routing schemes — which path and domain lead where, where caching is enabled, which rules work at the node closest to the user and which on the server side.
- Access policies and roles — who can change what, how keys are issued and revoked, how permissions are separated between your team and the contractor team.
- Environment variable description — a table with the purpose of each, whether it is required, the value format, and a list of places where it is used.
- Documentation — local startup, service composition, integration points, new version release procedure, and signs of typical failures.
- Update and rollback procedure — how to release a change and how to roll back to the previous version, and what happens to data in the process.
Schemes and procedures
Schemes are stored next to the code and updated along with it, rather than living in chat.
The update and rollback procedure is verified in staging before handover: an engineer who has not previously touched the project brings up a copy of the environment and rolls back a release according to the document, without clarifications from us.
Case study: peak load during sales
The client is a retail store with sharp peaks: on sale days, the flow of requests grew tenfold within an hour.
The storefront ran on two servers with a fixed ceiling; when it was exceeded, the catalog started returning errors, the cart lost items, and engineers manually brought up new nodes and waited for cache warm-up.
Adding servers "the day before" did not help: the exact minute of the peak could not be guessed, some resources sat idle, and some did not manage to come online in time.
We analyzed exactly where time was lost along the request path and redistributed the load across layers.
Metrics before and after
| Metric | Before | After |
|---|---|---|
| Catalog response time at peak | seconds, increasing with every minute of the surge | stable tenths of a second |
| Error rate during a tenfold surge | about 4% of requests | below 0.1% |
| Recovery time after failure | hours of manual actions | minutes, automatically |
| Preparation for the sale | night work with storefront downtime | without downtime, in stages |
We serve the storefront and personalized blocks closer to the user, while checkout, payment, and emails were moved to asynchronous processing with a queue and retries.
New application instances come online in seconds, so a tenfold surge is absorbed on its own, without monitoring duty.
The order of magnitude is confirmed by public measurements: Cloudflare publishes open statistics on the difference between serving from the edge and from a single center — radar.cloudflare.com.
For the client, this means the sale is no longer an emergency mode: the application holds the peak, and the team focuses on development rather than firefighting.
How an edge function differs from a serverless function
The point where code executes is determined by the platform, not by the code. A serverless function is deployed in a chosen region: the user's request travels over the network to that region and only begins processing there.
An edge function executes at the node closest to the user — the network path is reduced from hundreds of milliseconds to tens, and this is visible in time to first byte.
Their startup times differ in nature. A serverless function lives in a container that the platform stops when idle: the first request after a pause gets a cold start and adds tens or hundreds of milliseconds to the response.
Edge environments keep isolates warm, so startup there is measured in single-digit milliseconds, but in exchange for strict limits on execution duration and memory size.
Only lightweight data is near the user: request headers, geodata by address, static cache, replicas of key-value stores. The main database remains in the region, and if an edge function makes a synchronous request to it, the latency gain is eaten up by the return trip. Therefore, the decision is made based on data, not preference.
Long-running computations, multi-write transactions, file operations, and background tasks are not moved to the edge. Heavy dependencies and native modules increase bundle size and startup time, and state between requests is not preserved predictably there. Business logic, database access, and integrations remain on serverless functions.
The edge handles token validation, redirects, header personalization, cache delivery, and traffic splitting.
The boundary follows a simple rule: if the response is assembled from data located near the user, it is edge; if full context from the database is needed, it is a serverless function.
Technical doubts before starting
Doubts at the start are almost always the same — and justified. We work through the five questions below before the first line of configuration: in the serverless model, the answers to them determine the architecture, not the other way around.
Can we move the load back to a dedicated server?
Yes, the reverse migration is built in from the start: logic lives in portable handlers, and storage operations go through our own interfaces. The move takes days, not months.
Can we run it hybrid — leaving some routes on our own infrastructure?
Yes. We keep the admin panel, reports, and heavy background tasks on dedicated servers, while moving public pages and external event handlers to the edge.
Where are keys and secrets stored?
In a secure vault, injected at call time: they do not end up in the repository or build variables. Role-based access, key rotation, access log.
How does the application work with the database from different regions?
Reads come from the nearest replica, writes go to the primary database, and connections go through a pool. For operations requiring strict consistency, we keep a single region — we state this limitation before starting.
Who is responsible for rolling back changes?
We are. Versions are immutable and tagged; rollback is switching traffic to the previous one in minutes. The database schema changes only with reverse migrations tested on a copy.
Let's discuss the task and come back with an estimate
For the estimate to be based on your environment rather than a standard template, an initial set of inputs is enough — some data is usually already in the repository and observability panel. Send what you have on hand; we will clarify the rest in conversation.
- the current backend architecture: which services exist, what lives in a single process, where shared state is stored;
- load profile: peak requests per second, daily and weekly rhythm, seasonal surges;
- list of routes and their nature: static file serving, database reads, heavy computation, calls to external systems;
- data constraints: where data is allowed to reside, encryption requirements, retention and deletion terms;
- availability requirements: acceptable downtime and priority between regions;
- external constraints: what cannot be changed in contracts for mobile clients and partner integrations;
- access: repository, staging environment, observability panel, read rights for configuration schemes.
Based on these inputs, we will determine which routes move to the serverless environment and edge computing, and which remain on persistent processes — usually background tasks and long-lived connections.
We will come back with a work estimate and implementation plan: migration order, rollback points at each step, set of checks, and traffic switching procedure. Describe the task — we will discuss the details and arrange a call.







