AWS Lambda runs code in response to events and charges for requests and execution resources without requiring server management.
A Lambda function has code, configuration, an execution role, resource limits, and one or more event sources. API Gateway, EventBridge, SQS, S3, and DynamoDB Streams are common triggers.
Reliable serverless systems expect retries, duplicate delivery, partial failure, throttling, and downstream limits instead of treating every invocation as a one-time request.
Lambda fits short, event-driven, horizontally scalable work. Keep handlers focused and move durable state to a database, queue, object store, or workflow service.
The execution role grants the function access to AWS APIs; a resource policy controls who may invoke it. Store secrets outside environment variables when rotation or stronger controls are required.
Invocation behavior differs by source: synchronous callers receive errors directly, asynchronous events may retry, and queue mappings can process batches repeatedly. Handlers should recognize duplicate work safely.
Use structured logs, metrics, traces, and correlation IDs to follow an event through integrations. Watch duration, errors, throttles, iterator age, concurrency, and downstream response time.
A synchronous invocation keeps the caller waiting for the function response; errors return to that caller, which decides whether to retry. An asynchronous invocation queues the event in the Lambda service and can retry function errors according to its configuration. Event source mappings poll streams or queues and invoke functions with batches under source-specific rules.
The trigger determines delivery, retry, ordering, batch, and failure behavior. API Gateway, S3, EventBridge, SQS, and DynamoDB Streams do not become equivalent merely because they invoke the same handler. Document payload schema, maximum useful age, duplicate policy, ordering need, and failure destination for each source.
Give every event a correlation or operation identity and validate it at the handler boundary. Do not place a large payload in an event when an object reference is the better contract. Version event schemas so producers and consumers can deploy independently.
Lambda creates execution environments, initializes runtime and code outside the handler, then invokes the handler one or more times while the environment remains reusable. Reuse clients and immutable configuration initialized outside the handler, but never assume a warm environment will exist or that in-memory state is a durable source of truth.
Initialization work contributes to cold-start latency. Keep dependencies and startup work bounded, initialize lazily only when that improves the actual path, and measure by runtime, architecture, package, network configuration, and traffic pattern. Provisioned concurrency is an operational trade-off for workloads that need pre-initialized capacity; it is not the default answer to inefficient initialization.
Temporary files in the execution environments ephemeral storage may be reused by later invocations in that environment, but they can disappear at any time and must not leak data between tenants or requests. Close or reuse connections intentionally and make cleanup safe when execution is frozen or terminated without a graceful callback.
Memory configuration also affects available CPU and other execution resources, so tune it from cost and duration measurements rather than choosing the smallest value. Set timeout below the caller or queue deadline with enough margin to record failure and avoid work continuing after the upstream has abandoned the request.
Bound input size, decompression, parsing, outbound calls, and retries. One slow dependency can consume concurrency until functions throttle. Use connection and request timeouts, a retry budget with jitter, and circuit or queue controls where appropriate. A Lambda timeout can leave an external side effect completed even though the handler never recorded success.
Attach a function to a VPC only when it needs VPC resources or controlled egress. Plan subnet address capacity, security groups, DNS, endpoints, NAT, and dependency routes. VPC connectivity does not make the function private from invocation; invocation authorization and the network path solve different problems.
Idempotency means repeated delivery produces the same accepted business effect, not merely that the handler can run twice without crashing. Use a stable operation key, conditional database write, or idempotency record whose lifetime covers the possible redelivery window. Store the outcome when duplicate callers need the original response.
For SQS event source mappings, successful deletion follows successful batch processing. Partial batch response can report only failed records so successful messages are not retried, provided the handler and mapping use the supported contract. Queue visibility timeout, function timeout, batch size, concurrency, redrive policy, and downstream capacity must be designed together.
Stream mappings preserve ordering within their source units and can be blocked by a failing record. Configure bounded retry, record age, bisect behavior, and failure destinations where supported, then test poison data. A dead-letter queue or destination is a recovery queue, not a deletion mechanism; it needs an owner, alarm, inspection, and replay procedure.
Concurrency is the number of execution environments processing requests at one time. Account-level capacity, reserved concurrency, provisioned concurrency, scaling behavior, and source controls interact. Reserved concurrency can guarantee an upper boundary for one function and protect capacity for others, but setting it too low creates throttling.
Scale from downstream capacity backward. If a database accepts 100 useful concurrent operations, allowing thousands of functions to open pools or retry will amplify failure. Use SQS, maximum event-source concurrency, reserved concurrency, connection proxies, or application admission control to keep work within the dependency envelope.
Monitor concurrent executions, throttles, queue age, iterator age, duration percentiles, errors, and downstream saturation together. A low function error count during heavy throttling does not mean users are healthy. Load-test bursts, steady traffic, dependency slowdown, and recovery after backlog.
Publish immutable function versions and use aliases when traffic shifting or stable integration targets are required. Deploy code, layers, configuration, and IAM changes as one reviewed release plan. A rollback to old code can still fail if an event schema, environment value, or database migration is no longer compatible.
Use structured logs with request and business operation IDs, embedded or custom metrics for domain outcomes, and traces where cross-service latency needs explanation. Set log retention and redact event fields. Alarm on user-visible errors, throttling, backlog age, destination failure, and dependency health rather than only on raw invocation failure.
Exercise one synchronous error, duplicate asynchronous event, poison batch record, timeout after an external side effect, concurrency throttle, and rollback. Verify both the caller outcome and persisted state. A serverless system is production-ready when the team can find, contain, replay, and explain failed events without editing the function live.
Review runtime support dates and rebuild functions before a runtime becomes unsupported; a platform-managed runtime still leaves dependency and compatibility testing with the application team.
A practical serverless lesson should show not just the function, but also the event rule and invocation permission that cause it to run.
aws events put-rule \
--name nightly-orders-summary \
--schedule-expression "cron(0 2 * * ? *)"
aws lambda add-permission \
--function-name orders-summary \
--statement-id allow-eventbridge \
--action lambda:InvokeFunction \
--principal events.amazonaws.com \
--source-arn arn:aws:events:us-east-1:123456789012:rule/nightly-orders-summary
A queue-triggered function charges an invoice and the same message can be delivered more than once.
Constraints: The provider call is not inherently idempotent; partial batches must not replay successful records.
Decision: Persist the invoice ID as an idempotency key before the charge and return only failed message identifiers for retry.
Verification: Two deliveries create one charge, successful records leave the queue, and the failed record is retried alone.
Failure test: Crash after the provider accepts the charge and prove the replay reads the stored result instead of charging again.
Expected evidence: Two deliveries create one charge, successful records leave the queue, and the failed record is retried alone.
Explore 500+ free tutorials across 20+ languages and frameworks.