GAP·MAP

Serverless architecture

[ junior depth ]

A function runs because something happened

In functions as a service (FaaS), you deploy a handler and configure events that invoke it. An HTTP request, a queue message, a file upload, or a schedule can be that event. The provider runs the handler and manages the execution environment underneath it. AWS describes Lambda as code that runs in response to events. Azure Functions scales its function apps according to the events that trigger them.

The useful boundary is the handler invocation. It receives an event, performs bounded work, records any lasting result, and returns. Event-driven does not mean every invocation is asynchronous. An HTTP caller can wait for a response, while a queue-triggered function can run without a user waiting on it.

Wrong "Serverless means there are no servers."

Right The provider manages the servers and limits your access to them. AWS Lambda, for example, hides decisions about hardware, manages its integration with API Gateway, and controls the placement of execution environments.

Stateless code can still use state

AWS says a standard Lambda function should assume its environment exists for one invocation. Permanent changes must be committed to durable storage before the handler exits. A database, object store, or queue can hold that state.

An execution environment may be reused. Reuse makes it reasonable to keep an SDK client or connection outside the handler so initialization can be shared by later invocations. It does not make global memory durable.

const database = createDatabaseClient(); // reusable connection setup

export async function handler(event) {
  const order = await buildOrder(event);
  await database.orders.put(order); // durable result before return
  return { id: order.id };
}

Wrong "A global array is safe storage because warm functions keep their memory."

Right Warm reuse is an optimization that may happen. Code correctness cannot depend on it.

Cold starts add work before the handler

When AWS Lambda needs a new execution environment, it downloads the code, starts the environment, and runs initialization code before invoking the handler. That initialization adds latency. A reused environment skips most of that setup, so the later invocation is called warm.

Package size, imported dependencies, and initialization work affect AWS Lambda initialization latency. Keep startup work focused. If a synchronous API has a strict first-request latency target, AWS provisioned concurrency can prepare a chosen number of environments before requests arrive. Azure offers prewarmed or always-ready instances on some hosting plans.

Prepared capacity costs money. It also covers only the configured pool. Extra traffic may need on-demand environments.

Automatic scaling still has ceilings

The platform can add execution environments as concurrent work rises. Limits remain: an account or function can have a concurrency quota, scale-out can have a rate limit, and the event source may impose its own behavior. Once the applicable limit is reached, work can be throttled or delayed.

Standard FaaS often fits short event handlers and uneven demand well. A continuously running process or software that needs persistent process memory points toward another compute model. AWS's decision guide makes this split explicit for standard Lambda functions: Lambda targets short-duration event-driven tasks, while Fargate targets long-running containerized applications. AWS also offers Durable Functions for stateful workflows that can run for up to one year. Their checkpointed execution model is different from relying on the memory of a reused standard execution environment.

[ middle depth ]

Cold starts happen during scale-out too

An idle service scaling from zero is the obvious cold-start case. New concurrency can cause the same latency while a function is busy. AWS Lambda creates more execution environments as concurrent requests rise. Each new environment runs initialization before it can invoke the handler.

Move reusable setup outside the handler to amortize it across warm invocations. AWS lists SDK clients and database connections as examples of static initialization. Keep permanent business state out of that scope.

const client = createStorageClient();

export async function handler(event) {
  const existing = await client.get(event.id);
  if (existing) return existing;

  const result = await processEvent(event);
  await client.put(event.id, result);
  return result;
}

The code still needs a concurrency-safe idempotency design in its backing store. The example only shows where reusable setup and durable state belong.

Wrong "Calling the function every few minutes eliminates cold starts."

Right Provider-managed reuse is not a contract. AWS recommends provisioned concurrency when a workload needs predictable start times. Azure documents prewarmed or always-ready capacity on specific plans.

Warm capacity and maximum concurrency solve different problems

On AWS Lambda, provisioned concurrency is a number of pre-initialized environments. Reserved concurrency sets the function's concurrency allocation and maximum. Suppose a function has 20 provisioned environments and available unreserved concurrency. If 35 requests overlap, the first pool can be ready, while overflow can run on newly created environments and may experience cold starts.

A maximum protects scarce downstream capacity. If each execution environment can open a database connection, unbounded scale-out can exhaust the database before the function platform reaches its own limits.

Choose the cap from measured downstream capacity, then decide how callers should experience saturation. Depending on the integration, that can mean throttling, queueing, or retries. Azure also documents that one Functions host instance can process multiple messages or requests and that scale behavior depends on the trigger and hosting plan. One invocation per machine is not a portable model.

Wrong "Autoscaling means capacity is unlimited and immediate."

Right Autoscaling works within quotas, configured maxima, and scale-out rates. Those values are provider and plan specific.

Retries change handler correctness

AWS warns that event source mappings process events at least once and can deliver duplicates. Lambda application guidance therefore recommends idempotent handlers: receiving the same event again should not change the result beyond the first successful processing.

A charge operation needs idempotency enforcement outside the execution environment. A process-local set fails when another environment receives the retry or the original environment disappears. When the payment API provides an idempotency-key contract, pass the event identifier to that API:

export async function handler(event) {
  return payments.capture(event.payment, {
    idempotencyKey: event.id,
  });
}

If the side-effecting service has no idempotency-key contract, the application needs a durable protocol that records pending and completed work and can recover an interrupted attempt. A read followed by an unrelated write is insufficient: two invocations can both proceed, or a crash after the write can suppress work that never completed.

What the bill measures

AWS Lambda Functions pricing has two main usage dimensions: requests and execution duration measured in GB-seconds, based on allocated memory. Provisioned concurrency adds a charge for the configured concurrency and the time it remains configured. Other services in the path, such as storage, queues, logs, API gateways, and data transfer, have their own charges.

Pay per use describes the meter, not the outcome. Sporadic work can benefit from scaling down between events. For a steady workload, compare the full measured bill with continuously allocated compute. Duration, memory, request volume, prepared capacity, and attached services all belong in that comparison.

[ senior depth ]

Scaling is a distributed-systems boundary

Function concurrency is the number of invocations in flight, not requests per second. A useful estimate for synchronous work is arrival rate multiplied by average duration, but capacity planning cannot stop at an average. Bursts and tail duration determine how many invocations overlap.

AWS Lambda currently documents a default regional account concurrency quota of 1,000, with quota increases available, plus function-level scaling controls. AWS warns that new accounts can begin with reduced concurrency quotas, so capacity planning must use the account's current quota. Azure Functions uses the function app as its unit of scale on most plans and applies trigger-specific heuristics; Flex Consumption can scale many non-HTTP functions independently. These are contracts for particular services, not properties of FaaS as a category.

Before raising a limit, map each concurrent invocation to downstream work: database connections, API calls, queue visibility time, and write contention. A function platform can remain healthy while it overloads a dependency.

export async function handler(event) {
  await limiter.acquire("payments-api");
  try {
    return await payments.capture(event.paymentId);
  } finally {
    await limiter.release("payments-api");
  }
}

This limiter must use shared external state if it coordinates multiple execution environments. It also needs leases or another recovery mechanism so a terminated invocation cannot hold capacity indefinitely. Provider concurrency caps are preferable when they express the needed boundary directly.

Cold-start mitigation spends something

Reducing a deployment package and initialization work attacks initialization duration. Provisioned or always-ready capacity attacks the chance that a request needs on-demand initialization. These controls answer different questions.

AWS bills provisioned concurrency for the amount configured and the period configured, in addition to request and execution charges when the function runs. Traffic beyond the provisioned pool can still use on-demand capacity when available. A scheduled baseline can suit a predictable peak, but the schedule and level should come from measurements.

Wrong "Provisioned concurrency removes cold starts from the architecture."

Right It pre-initializes a bounded pool. Overflow may initialize on demand, and AWS documents that environments can be recycled or reset.

Cold starts matter most when their latency is visible to a synchronous caller or consumes a tight processing deadline. For asynchronous work with sufficient queue tolerance, paying for idle warm capacity may solve a problem the user cannot observe.

Statelessness is about authority

An environment can retain global objects and temporary files when AWS Lambda reuses it. Treat those as caches. They may improve latency, but no authoritative result or workflow position can depend on their survival.

Long workflows also need durable coordination. AWS recommends Lambda Durable Functions or Step Functions for branching, retries, and execution state instead of ad hoc orchestration inside standard functions. Durable Functions can run for up to one year and persist checkpoints; that is a separate programming model, not warm-environment reuse. This does not mean every multi-step request needs a workflow service. It applies when the workflow itself must survive failures and resumptions.

Duplicate delivery turns side effects into the hard part. Put idempotency state in durable shared storage, define how long it remains valid, and make transitions atomic with respect to competing invocations. The protocol must distinguish pending from completed work and recover an invocation that fails after claiming the event but before completing the side effect.

Wrong "The platform retries failed events, so the application is reliable by default."

Right Retries improve delivery attempts. The handler must make repeated delivery safe, and operators still need failure destinations or another documented recovery path for the chosen integration.

Where serverless stops fitting

AWS's compute decision guide places Lambda on the short-duration, event-driven side and Fargate on the long-running, persistent-process side. Standard Lambda also has a 15-minute invocation limit. That makes a continuously running worker with long tasks a poor direct migration target.

The economic boundary needs measurement. AWS Lambda meters requests and GB-seconds, while provisioned concurrency and connected services add separate charges. Sporadic arrivals and independent bounded handlers match this shape. Steady high utilization, long execution, persistent process requirements, or specialized runtime and resource control can favor container or instance-based compute.

There is no architecture-wide rule that functions are cheaper. Compare request count, memory, duration distribution, provisioned capacity, storage and messaging operations, observability, and data transfer. Then include the operational work each option removes or introduces.

dig deeper

Primary sources behind these notes - the specs and official docs worth reading in full.

beta

The interviewer part is in the works.

The diagnostic, personal maps, and AI mock interviews are being finished right now. The notes stay free either way. Leave an email and you'll get the first-cohort invite, plus a month of Pro when it opens.

builds on

more Infrastructure & Ops

was this useful?