Global outage event
If you're experiencing issues with your AWS services, then please refer to the AWS Health Dashboard. You can find the overall status of ongoing outages, the health of AWS services, and the latest updates from AWS engineers.
re:Invent 2025 - Serverless patterns for large-scale applications with AWS Lambda
Growing a serverless application from a handful of functions to hundreds creates real organizational, code management, and deployment challenges. This post covers the patterns presented in session CNS372 to help you structure, share code across, and deploy Lambda functions at enterprise scale.
Building a serverless application starts simply enough. You map one piece of functionality to one function, connect it to a data store, and ship it. Scaling that approach to an enterprise e-commerce application with separate workflows for cart management, product lookup, inventory, payment, and order fulfillment is where three persistent problems emerge: how to organize hundreds of functions in a maintainable structure, how to share code across them without constant duplication, and how to deploy consistently across development, staging, and production environments. Lefteris Karageorgiou, Senior Solutions Architect at AWS, and Michelle Chismon, Senior Solutions Architect at AWS, tackled all three at re:Invent 2025. In this post, we'll walk through each pattern they presented and the specific techniques you can apply immediately to your own serverless applications.
Organizing Lambda Functions at Scale
The default serverless pattern maps one function to one HTTP operation. This is clean in principle, but as your application grows you can end up with dozens of functions scattered across a single repository, all sharing one CI/CD pipeline. A broken deployment means everything is at risk, and navigating the codebase becomes genuinely difficult when you have no organizational structure.
The first step is applying bounded context, a principle borrowed from microservices architecture. Functions that operate on the same domain, share the same data model, and belong to the same team should live together. In the e-commerce example from the session, the add-to-cart, remove-from-cart, and get-cart functions all work on the shopping cart domain. Grouping them into a cart service folder, with their own AWS Serverless Application Model (SAM) template and their own Git repository, allows that service to be developed, deployed, and scaled by one team without touching anything else. Applying the same logic to inventory, payments, and products gives you a multi-repo, microservices-aligned structure where each domain is fully independent.
Once your functions are logically grouped, you can reduce the total number of them using the Lambda Web Adapter pattern. Instead of one AWS Lambda function per HTTP method, you run a traditional web framework inside a single function and let it handle routing internally. In the session demo, the add-to-cart and remove-from-cart functions were combined into a single manage-cart function using FastAPI and Mangum as the web adapter. The immediate benefit is that both operations share a warm execution environment. After a POST request initializes the function, a subsequent DELETE request on the same instance skips the cold start entirely, dropping response time from over a second to around 0.2 seconds.
Cold start behavior is worth understanding before adopting this pattern. Bundling a web framework and its dependencies into one function increases the deployment package size, which makes the initialization time on a fresh execution environment longer than a leaner single-purpose function. The merged function in the session demo took around 2.8 seconds on cold start versus 1.3 seconds for the original smaller function. That trade-off pays off when the combined operations are called frequently enough that the execution environment stays warm. The session includes an important caution: do not build a monolith. Keep any merged function scoped to a single bounded context, and monitor its deployment package size and memory usage continuously.
For the simplest operations, removing Lambda entirely is the most effective optimization. Amazon API Gateway direct integrations allow API Gateway to call AWS service APIs without a Lambda function in the path. The get-cart function in the session demo fetched a single item from Amazon DynamoDB and returned it with no transformation or business logic. Replacing it with an API Gateway integration that calls DynamoDB's GetItem API directly eliminated the cold start, reduced cost by removing Lambda invocation charges, and removed the concurrency limit from that request path. Response time dropped from 1.3 seconds to 0.2 seconds on the very first call. This pattern fits simple create, read, update, and delete operations where there is no business logic or complex error handling involved. Avoid it when you need transformation, validation, or error handling beyond what HTTP status codes can express.
Reusing and Removing Code
Code duplication accumulates quickly across Lambda functions. The same database connection logic, authentication checks, or error formatting ends up copied into dozens of separate functions. When that shared logic needs to change, you have to find and update every instance.
Lambda Layers solve this by giving you a shared library you attach to multiple functions. A layer is a zip archive of shared dependencies, utilities, or custom runtimes that you deploy once and reference by Amazon Resource Name (ARN) in any function that needs it. The session highlighted Powertools for Lambda as a practical example: an open source toolkit that bundles structured logging, metrics, and tracing best practices into a layer you can attach to any function with a single ARN. You can build your own layers the same way, packaging your authentication logic, database connection helpers, or any other shared code into a zip archive and deploying it as a versioned layer. Layers are subject to a five-layer limit per function and count toward deployment package size limits: 50 MB zipped and 250 MB unzipped. They also add to cold start time because layer contents are downloaded when a new execution environment initializes, so be selective about what you put in them.
For orchestrating multi-step workflows, AWS Step Functions lets you replace code with direct service integrations. The order processing workflow in the session required submitting an order, reserving inventory, processing payment, and fulfilling the order. Several of those steps were simply writing to or reading from DynamoDB. Replacing those Lambda functions with direct SDK calls inside a Step Functions state machine reduces the amount of code to write and maintain. The steps that genuinely require business logic, like processing a payment, remain as Lambda functions with full error handling and validation. Everything else becomes a managed service call with built-in retry logic and error handling that Step Functions provides out of the box. The result is a workflow where the execution path is visual, the retry behavior is declarative, and the amount of custom code is smaller.
The broader principle here is that removing code entirely is often better than reusing it. When a Lambda function does nothing more than pass a request to another AWS service and return the response, that function adds latency, cost, and maintenance overhead without contributing value. Direct integrations, whether through API Gateway or Step Functions, offload that work to managed services that are designed for it.
Deploying Consistently Across Environments
Consistent deployments become harder to maintain as your application grows across development, staging, and production environments. Two common mistakes undermine this consistency: keeping all environments in one AWS account, and duplicating infrastructure templates or code per environment.
SAM templates provide four mechanisms that let a single template work across environments without duplication. Parameters capture input values at deploy time, such as the target environment name. Mappings translate those parameters into environment-specific values, for example 256 MB memory for development and 1024 MB for production. Conditions determine whether a resource gets deployed at all, which lets you skip expensive logging or monitoring infrastructure in non-production accounts. Globals set values that apply to every Lambda function in the template, such as runtime version, timeout, and environment variables, so you define them once instead of repeating them for each function. Together these features give you one authoritative infrastructure definition that adapts to each environment rather than separate templates you have to keep in sync.
For deployment pipelines, the session showed a GitHub Actions workflow that uses OpenID Connect (OIDC) for credential-less authentication to AWS, removing the need to store long-lived access keys as secrets. The workflow passes environment-specific variable overrides to SAM deploy at runtime. A human review gate before the production deployment step prevents automated promotion until someone verifies the development deployment completed correctly. This matters in practice because a pipeline that reports a successful deployment does not always mean the application is actually running as expected.
On account strategy, the session made a clear recommendation: at minimum, separate production from development and testing accounts. When all environments share one account, a mistake in development or a compromised credential can affect your production workload. For larger organizations managing dozens of services and teams, a multi-account structure adds operational complexity, but the isolation it provides becomes essential. The session acknowledged that for small teams or early-stage applications, a single account can be a reasonable starting point. The priority is that production runs in its own account, isolated from the environments where experimentation happens.
Conclusion
The three challenges in this session compound each other as your application grows. Functions without structure make code reuse harder. Duplicated code makes consistent deployments harder. Deployments without structure make everything harder to debug and maintain.
The patterns covered here address each layer directly. Bounded context gives you a logical grouping that maps to team ownership and independent deployment. The Lambda Web Adapter reduces the number of functions to keep warm and the number of execution environments to manage. API Gateway direct integrations and Step Functions SDK integrations remove Lambda from paths where it adds no value. Lambda Layers centralize the code that multiple functions genuinely need to share. Dynamic SAM templates with parameters, mappings, and conditions eliminate per-environment copies of your infrastructure definition. Deployment pipelines with OIDC authentication, variable overrides, and human review gates complete a consistent delivery process.
Lefteris and Michelle closed with a practical call to action: pick one pain point, apply the relevant pattern, and see what changes. You do not need to adopt everything at once. Start with whichever friction costs your team the most today, whether that is the time spent navigating a sprawling function list, tracking down duplicated logic across a codebase, or untangling environment-specific deployment failures.
Watch the full session recording: CNS372 - Serverless patterns for large-scale applications with AWS Lambda
- Language
- English
Relevant content
- Accepted Answerasked a year ago
- Accepted Answerasked 4 years ago
