DEV Community

Cover image for AWS re:Invent 2025 - Serverless patterns for large-scale applications with AWS Lambda (CNS372)
Kazuya
Kazuya

Posted on

AWS re:Invent 2025 - Serverless patterns for large-scale applications with AWS Lambda (CNS372)

🦄 Making great presentations more accessible.
This project aims to enhances multilingual accessibility and discoverability while maintaining the integrity of original content. Detailed transcriptions and keyframes preserve the nuances and technical insights that make each session compelling.

Overview

📖 AWS re:Invent 2025 - Serverless patterns for large-scale applications with AWS Lambda (CNS372)

In this video, AWS Senior Solutions Architects Lefteris Karageorgiou and Michelle Chismon address three key challenges in scaling serverless applications: organizing hundreds of Lambda functions, reusing code across functions, and maintaining consistent deployments. They demonstrate applying microservices principles with bounded context to structure functions, using Lambda Web Adapter to merge related functions and reduce cold starts, and implementing API Gateway direct integrations to eliminate unnecessary Lambda functions. The session covers Lambda layers for code reuse, Step Functions with SDK integrations to replace Lambda functions for simple operations, and strategies for dynamic deployments across multiple AWS accounts using SAM templates with parameters, mappings, conditions, and globals. Practical examples include refactoring an e-commerce application's cart service and implementing GitHub Actions workflows with OIDC authentication.


; This article is entirely auto-generated while preserving the original presentation content as much as possible. Please note that there may be typos or inaccuracies.

Main Part

Thumbnail 0

The Challenge of Managing Hundreds of Lambda Functions at Scale

Imagine your manager comes to you and asks you to build a serverless platform for your new e-commerce application. So you start building a few Lambda functions, and after three months, you build hundreds of Lambda functions, which becomes very hard to manage. In this session, we will share serverless development practices to confidently scale your Lambda applications.

Hi everyone and welcome to CNS372, Serverless Patterns for Large-Scale Applications with AWS Lambda. My name is Lefteris Karageorgiou. I'm a Senior Solutions Architect with AWS, and I'm thrilled to be joined by Michelle. Hey everyone, I'm Michelle Chismon, a Senior Solutions Architect. I'm also at AWS.

Thumbnail 80

Now, when companies want to build or migrate their applications to serverless and AWS Lambda, they usually start by mapping one functionality to one Lambda function. We call this single-purpose Lambda functions. Let's take an example of an e-commerce use case where we might have the following architecture. Now in this architecture we see a few Lambda functions. For example, we see a Lambda function to get a product, to add something to our shopping cart, to get something from our shopping cart, or remove something from there, or we have a few Lambda functions to actually purchase something. So we go through a series of steps which is submitting an order, reserving the inventory, processing the payment, or fulfilling the order.

Now, all these Lambda functions are backed up by Amazon API Gateway, which serves as the front door for our API layer. And we have a few endpoints there that actually are integrated and call the Lambda functions. Now each Lambda function is also saved to a different DynamoDB table. For example, anything which has to do with products is saved to a Products table, or anything that has to do with carts is saved to a Carts table in DynamoDB.

Thumbnail 170

Now, this approach of having one functionality mapped to one Lambda function is perfectly fine, and many companies do exactly that. However, as customers grow and add more features, they end up with hundreds and sometimes thousands of Lambda functions. And this introduces three major pain points that we hear consistently from our customers. The first is how do I properly structure and organize many Lambda functions, like hundreds of Lambda functions. The second one is how do I reuse code across those functions? And the third one is how do I maintain consistent deployments across different environments, meaning development, staging, or production. And this is exactly what we're going to solve during the session. By the end of this talk, you'll have practical patterns you can apply immediately to your serverless applications.

Thumbnail 240

Thumbnail 250

Thumbnail 270

Understanding Single-Purpose Lambda Functions and Cold Start Performance

Now, let's tackle the first problem. How do I properly structure and organize many Lambda functions? Now, in this session, I'm going to use Kiro, which is our new generative AI assistant that is based upon Visual Studio Code and is now generally available for you to use, and it's really awesome. So here I have all the Lambda functions I showed you at root level in separate folders. So let's take a look. We have for example the AddCart Lambda function here, which has a very basic functionality. So what it does is actually getting an input from Amazon API Gateway, it is validating the input, it is creating a cart item to be saved to DynamoDB, and it goes there and saves it. Nothing fancy, very simple one, the same with RemoveCart as well, right? So you remove something from DynamoDB, or the GetCart, you get a cart from DynamoDB based on a cart ID. Nothing fancy, very simple Lambda functions.

Thumbnail 290

I also have here a template.yaml, which is a SAM template. Now SAM stands for Serverless Application Model, and it is an open source framework which makes it easy to build serverless applications. It is essentially an extension of CloudFormation, and it consists of two parts.

Thumbnail 310

One, you can write your infrastructure as code, as we see here in the template.yaml. And the other part is that you can use CLI commands to help you with local development, testing, and deployment as well. Now, for example, some CLI commands you can use with SAM to help you with your serverless applications. You can initialize a new serverless project using the SAM init command, and this will create a code base you need to start working with serverless applications. You can do some build to build your project. Whatever programming language you might be using, if it's Java, Python, it will build it for you. It knows how to build it. And also you can do deploy where you can deploy your serverless application to AWS.

Thumbnail 370

Thumbnail 380

Thumbnail 400

But now let's take a look at the SAM template that we have here. So it's a very simple SAM template which creates an Amazon API Gateway over here and all the Lambda functions that I showed you. We have a root level. For example, you have the GetCart Lambda function here, and you also create an API Gateway endpoint that is integrated with this Lambda function with these five lines of code. So we have here all our Lambda functions, and at the bottom we have the DynamoDB tables as well.

Thumbnail 420

Now, you can visualize that one by using the Infrastructure Composer. Over here, you see I have all my Lambda functions, I have my DynamoDB tables, and I have my API Gateway and the endpoints I expose. Now, this code base is deployed to a single Git repository. I have already deployed that to my account, so let's run some tests against some endpoints.

Thumbnail 450

Thumbnail 470

So I have created a test script here. So what I'm going to do is I'm going to call my test script, and I'm going to do the single purpose Lambda functions that we have here, and I'm going to add something to our DynamoDB table using the POST cart endpoint that I have in my API Gateway. So what this does, it's actually doing a curl which is sending like a payload there. Now, as you can see, I've added something to my DynamoDB table using the Lambda function I wrote, and it took 1.2 seconds to finish.

If I run that again, I'll see that it takes 0.2 seconds to finish, so it's much, much faster. So why is that? Because of the cold starts. And so what is a cold start? Cold start is essentially the initialization time it takes for a Lambda function to download the code, to start up the environment, the runtime environment, for example, JVM if you're using Java, right? So prepare this execution environment as we call it inside a micro VM and then run it. So this initialization time, the first time is called the cold start. Now the second time this is already warmed up, so we won't incur a cold start again.

Thumbnail 550

I can also do a GET here. Now since this GET is another Lambda function, then I will incur another cold start, right? So this also takes 1.3 seconds to finish. If I do it again, I won't see the cold start. It will be much, much faster, 0.3 seconds. Now, in our code, we have only a few Lambda functions, but if you have 50 or 100 or 200 Lambda functions, this becomes really unmanageable. Now, the code base is going to grow, and you will have a single Git repository, you will have one massive CI/CD pipeline, and if anything breaks, everything is at risk.

Thumbnail 610

Applying Microservices Principles and Bounded Context to Serverless Architecture

So what is the solution to this? We are going to apply the microservices principles and the bounded context to our serverless architecture. Now, let's take a look at those three cart functions that we have here, the AddCart, the GetCart, and the RemoveCart. Now they all operate on the same domain, which is the shopping cart. They share the same data model, the same DynamoDB table, and they are likely owned by the same team.

Now let's group those into a separate folder. I'm going to create a new folder, which I'm going to name cart service. What I'm going to do is take the get cart method and move it there. I'm going to get the remove cart method and move it there as well, and I'm going to get the add cart method and move it there. So now we have a cart service folder and all the cart related functions live here.

Thumbnail 700

Now you will apply the same principle to the other functions, so everything that is related to the inventory you will create a different folder and put everything there. The same for orders, the same for payments, the same for products. Since we have applied the microservices principles, then we need to split the resources into multiple repositories. For that, I would have to create one SAM template for each microservice and repository. What I'm going to do here is create a new template here inside the cart service folder.

Thumbnail 710

Thumbnail 720

Thumbnail 730

Thumbnail 740

Inside this template YAML file, I'm going to take only the cart related Lambda functions. I'm going to take the Amazon API Gateway API and the DynamoDB cart table only. I'm going to remove everything else from here, and I'm going to leave here only the three Lambda functions.

Thumbnail 760

Thumbnail 770

Thumbnail 780

Now, this template is much more clean. Usually in a microservices architecture you don't expose the databases or anything that is related to the data directly, but you do it through APIs. Because in here what you could do is you could output something from your SAM template in order for other templates to have that as an input. For example, you could output here the carts table or even the Lambda functions, but you don't want to do that in a microservices architecture. You want everything to be exposed via an API.

Thumbnail 810

Now, the benefits of this are that each service can be developed, deployed, and scaled independently. It can be owned by different teams, and the shared dependencies no one else will be using. This is how you actually scale serverless architectures organizationally. As I said, this cart service will be deployed into a different Git repository, and you will do the same for all the domains that you might have in your microservices architecture.

Thumbnail 850

Now let's run some tests against this new cart service microservice that we have. I have deployed that to another AWS account. I'm going to do some tests again, and I'm going to do a test for the microservice add function. Will we see any change? No, it will be exactly the same because we have again three Lambda functions. We have a cold start for each Lambda function, so we'll see the same duration for each Lambda function. So 1.3 seconds to add something, 1.3 seconds again to get something. Although we've done some optimizations on the organization level, we haven't done any optimizations on the Lambda functions.

Reducing Lambda Functions with the Lambda Web Adapter Pattern

Now, we'll talk about an optimization technique for Lambda, which is to reduce the actual number of the Lambda functions we have. A pattern that is gaining traction is called the Lambda web adapter. Now, instead of having one function per HTTP method,

you can run a traditional web framework inside your Lambda function and write route requests internally. Let me show you how you can do that. What I'm going to do here is merge the AddCart and the RemoveCart into a single ManageCart function. I already have my code here, so let me do that. I'm going to remove the AddCart and the RemoveCart because I'm going to create a single ManageCart function and I'm going to put it under the cart service.

Thumbnail 960

Thumbnail 970

Thumbnail 1000

Thumbnail 1020

Thumbnail 1030

Let's see what we've done here. In order for the Lambda web adapter to work, you have to use a library that allows you to run a traditional web framework inside a Lambda function. Here we are using FastAPI, which is a very commonly used library for Python, and we are using also Mangum as the web adapter for FastAPI. What we are actually doing here is the following. As you can see here, we have one function, which is the add_to_cart function, which is getting all the requests from Amazon API Gateway from a POST method to the /cart endpoint. We also have another function, another method which is getting all the requests from API Gateway through the DELETE /cart endpoint. So this Lambda function is actually getting two endpoints from API Gateway into a single Lambda function, whereas in the previous example, we had one entry point for our two Lambda functions.

Thumbnail 1080

From three Lambda functions that we had, the AddCart, the GetCart, and the RemoveCart, we're now down to two Lambda functions. We have the same functionality but less infrastructure overhead. Now let's run some unit tests to see what we have achieved. I'm going to clear that, so I'm going to do the test again. I'm going to do the adapter, and I'm going to add something into our DynamoDB table. Now the response took 2.8 seconds to complete, whereas in the previous example it took 1.3 seconds to complete. Why is that? Because now we have a bigger Lambda function. If your Lambda function is bigger because you have more libraries and a larger deployment size, then the cold starts will also increase. Be aware that the deployment size really matters for the cold starts in your Lambda function and for the initialization time.

But the cool thing about that now is that if I do a remove, since the add method and the remove method are in the same Lambda function, now the remove method will not incur any cold starts. So we have now 0.2 seconds, which is much, much faster. The benefits of using a web adapter is that you have fewer cold starts as we saw, so we have one function to keep warm instead of two. You can do shared initialization, so if you do database connections or SDK clients, these are initialized just once. You can use familiar patterns, for example, you can use FastAPI, you can use Express, you can use Spring Boot if you're a Java developer, or any web framework you know, and you can plug it into Lambda. It's also easier to test your Lambda functions because the routing logic is inside your Lambda function. You can mock everything or you can use unit tests within your Lambda functions.

But be careful, don't create a monolith with these web adapters. If you have, let's say, 20, 30, or 100 functionalities within your Lambda function, you'll have a huge deployment. So don't create a monolith. Keep your functions focused on the bounded context, on the microservices principles. Now, try to also monitor your function size and the memory all the time. And consider that one Lambda function failure might affect multiple operations if you use the web adapters.

Now some factors to consider when doing this merging and using the web adapters are the following. If you have common code dependencies, you can merge your functionality into one Lambda function. If you have common downstream dependencies, you can do that, right? You can group your functions based on the initialization time that you might have or the memory configuration that you want to configure to your Lambda function. Or maybe your IAM permissions you might want to set to your Lambda functions. All these are good examples to consider using the web adapters.

Thumbnail 1300

Eliminating Lambda Functions with API Gateway Direct Integration

Now, a very common scenario that customers use Lambda for is also to transport data from API Gateway to the downstream services. For example, let's take a look at our GetCart method. Here all we do is fetch data from DynamoDB and return it to Amazon API Gateway. We have no business logic. We are doing no transformation. We are doing nothing, just fetching something and returning it. Now, we can optimize that by removing the Lambda function and replacing it with what we call API Gateway direct integration.

Thumbnail 1370

Thumbnail 1380

So what is a direct integration? Direct integrations allow Amazon API Gateway to directly communicate with AWS services like DynamoDB, Step Functions, or others without needing an intermediary Lambda function. Let's do that for our GetCart function. So what I'm going to do is delete the GetCart function because I don't really need it. I will replace it with a direct integration from Amazon API Gateway, and what I'm going to do is get my template from here. And I'm going to put it in here and replace it with visual.

Thumbnail 1390

Thumbnail 1410

Thumbnail 1430

Thumbnail 1440

So let's see what we have here. So we have our Amazon API Gateway like we had in the previous example, but now what we see here is that whenever we're doing the GET endpoint from here, what we are doing is calling an Amazon API Gateway integration. And the integration is against DynamoDB and what we're doing is actually calling an API from DynamoDB which is the GetItem API. So API Gateway directly calls an Amazon DynamoDB API, which is the GetItem API. It sends the payload that we had and it also constructs the response as we see here. It uses a language called VTL, which actually creates the response from the DynamoDB response that we had.

Thumbnail 1460

Thumbnail 1470

Okay, so let's run again some tests and see what benefits we have in the GetCart method. I'm going to my test script again. And I'm going to do test direct integration and the GET method. So now what we see here, we see first of all, we don't see any Lambda invocation here. We call directly DynamoDB. So what that means is we don't see any cold start, right? So we see the first time we call that is 0.2 seconds, which is amazing, right? So the benefits here are significant. You have a lower cost, so you don't have any Lambda invocation charges. You have lower latency,

you have one less hop in your whole request path. You see no cold starts because API Gateway is always warm, right? And you also have better scalability because you have no Lambda concurrency limits to worry about. So when should you use this pattern? You should use it when you're doing simple CRUD operations, create, read, update, delete operations. You can use it when you have no business logic or validation, or you want the absolute lowest latency and cost. But try to avoid it when you need complex transformations or business logic is required, or you need error handling beyond the HTTP status codes.

Thumbnail 1570

So I'm going to take one question there. Can you connect from S3 to serve any request? So S3 really exposes APIs. You can expose an API from S3, so I wouldn't use API Gateway with S3 directly. Do you need mTLS authentication with that? Okay, so I'm not really sure if you can do it with S3, but I mean, API Gateway direct integration has more than 100 integrations. That's definitely one I think to dive into after the talk. Yeah, we can look into it.

Thumbnail 1610

Thumbnail 1620

So let's recap what we've done to solve the first pain point, which is organizing the Lambda functions. So we went from many scattered Lambda functions in one Git repository to a microservices and bounded context approach in a multi-repo architecture. We went to fewer Lambda functions using the Lambda Web Adapter pattern and to zero Lambdas for simple operations with the Amazon API Gateway direct integration. And you can do the same for the other Lambda functions as well. For example, the GetProduct, you can do the same API Gateway direct integration and remove the GetProduct Lambda function entirely.

Yes, so from the API Gateway, there's a timeout of 30 seconds, right? If you are using a lot of functions, 29 seconds. Yes, 29 seconds, yeah. So I mean that you can make it higher, right? Yeah, this is a soft limit nowadays, so you can increase that with a support ticket. Yes, yeah. With direct integration, if you have a lot of functions in between and you change it to be a direct integration, no, because 29 seconds is the API Gateway timeout. Lambda functions have a timeout of 15 minutes, but 29 seconds is API Gateway.

Thumbnail 1710

Reusing Code Across Functions with Lambda Layers

So what we've solved is how to properly structure and organize many Lambda functions. Okay, so let's say we've built now eight different Lambda functions. And whilst writing those Lambda functions, you've realized that actually, you've written the same database connection logic 15 different times. Does that sound familiar to anyone? Yeah. This is one of the complexities that we have when we start having these isolated Lambda functions, because they sound brilliant in principle. But when you've got the same thing that needs to happen across multiple different functions, you're ultimately having code duplication.

So in this section, what we're actually going to talk about is how we start to reuse code across functions and to try and reduce that code duplication you might find across your code bases. Is it all coming up now? Excellent. Okay, so the simplest way is to obviously have everything as a single purpose function, an isolated unit. And as we just mentioned, this can lead to duplicated code, which isn't particularly efficient. When you're becoming aware that you've got that duplicated code, you panic and you start throwing multiple different features into the same Lambda function to try and reduce that duplication, because they need the same connection logic. So it makes sense to have lots of different database connections in just one Lambda function, right? But then that leads to other complexities.

So if you do have 15 duplicated database connection logic and you need to update that, you've now got to find every single instance of that connection logic in your code and update it to be what you need it to be. And that creates a mess, and no one loves doing that. Everyone hates that. That's the boring part of programming, right?

So this is why code reuse matters. We want consistency and we want reliability across all of our code bases and across our deployments and our code. If we've got authentication logic that's in just one location, it means that our authentication is handled the same way across every single Lambda function. Likewise, if we're doing error handling and that's centralized, we can then make sure that our error handling is done exactly the same way across all of our Lambda functions.

So if we're talking about code reuse, then I'm sure a few of you have leapt to the conclusion of what it is we're going to talk about now, and that's Lambda layers. So Lambda layers are ultimately a shared library that you can share across all of your Lambda functions. You can bundle into this layer things like custom runtime, any of your dependencies, and you create that layer once but then have multiple Lambda functions use it. So it's a really, really powerful way of bundling together all of that code that you might need to use multiple times.

Thumbnail 1910

So to show you this, I'm actually going to leverage a Lambda layer that already exists. You might have heard of this layer already, and it's called Powertools for Lambda. This is actually a really cool Lambda layer, an open source toolkit, because it bundles a load of serverless best practices into your Lambda function, and you can utilize them right there. So things like logging and monitoring, there's these best practices already there, and I really encourage you to go and look into this in more detail because it's super useful. But I don't want to get sidetracked in this talk talking about it.

So because this Lambda layer already exists, when it comes to our template here with our Lambda functions, all I actually need to do is add the ARN to deploy it. But obviously that's great for something that's third party and already exists, but you might have the dependencies that you want to bundle yourself. But that's really simple to do because all you have to do is create a zip archive of the things that you want to bundle within your Lambda function and then deploy a layer as you would within your SAM template, your CloudFormation template, Terraform, however it is you write your own infrastructure as code. And it doesn't have to be within the same template. You can obviously deploy it in another template and then reference the ARN in another stack.

Thumbnail 1990

Thumbnail 2000

So because I can't remember ARNs off the top of my head to save my life, and this one is pretty complicated, I am just going to copy this one. I do apologize. And we're going to take this first Lambda function here, and we're just going to add the layer. I don't want conditions. Is anyone else getting really annoyed with the AI suggestions in IDEs at the moment? Okay, right. So that's now a layer added into our GetCart function.

Thumbnail 2050

And obviously this is our SAM template, so we can go ahead and just do a SAM build. Not for the config file, thank you, AI. And then that'll go ahead and build, and then I can do a quick SAM deploy. Now you can give me a config file. There we go. And then that'll go ahead and run the SAM deploy. And once it's deployed, that layer will be attached to our Lambda layer. Now, you don't want to see that deploying, so instead we're just going to talk a little bit about Lambda layers.

So there are some key things we need to think about with Lambda layers because they're not a catch-all solution, right? You can only have five layers per Lambda function, and that's a real limitation. But in sort of relation to that, we also have deployment package size limits, and that doesn't change when you add a Lambda layer, right? So a zipped deployment package is capped at 50 megabytes, and unzipped, 250 megabytes. So don't think that you can just suddenly add every library or every runtime that ever existed into a Lambda layer and you can deploy it because of these deployment package size limits. So you do have to be strategic about what you put into a Lambda layer. Don't put everything in there, only put the things in that you need.

Lefteris talked a lot about cold starts in the beginning of this talk.

At the beginning of this talk, I mentioned that Lambda layers do impact your cold starts because anything that you bundle into your layer has to be downloaded on each fresh execution environment. So if you've put in a really large ML library, that's going to take a while to download and you're going to see some increase to your cold starts. And then finally, versioning is something to pay attention to, right? Because versioning can be an absolute godsend. You can do A/B testing, blue-green deployments. But you can also get yourself into a very sticky situation with versioning, because say for example, function A needs version 1 and function B needs version 2. Well now you're having to maintain two different versions of the same Lambda layer because two different functions need it. So make sure if you are using versioning with Lambda layers, you're doing so in a well thought out manner to avoid falling into those pitfalls.

Orchestrating Workflows and Replacing Code with Step Functions SDK Integrations

Now, this is code reuse. This is being able to use the same code across multiple functions, but only writing it once and only having it in one location. And Lefteris has already touched on a more radical solution, which is just, let's remove code altogether. So Lefteris talked to you about these SDK integrations between API Gateway and say, DynamoDB. That's great for a simple workload that's just making that one synchronous call. But sometimes you might actually have a series of calls that you need to make.

Thumbnail 2210

So if we think back to this architecture that Lefteris showed earlier, we have our order API here at the bottom. And there's a sequence of things that need to happen in order to fulfill a particular order. But several of these are still just making DynamoDB calls, and we're using a full Lambda function that's got code that we have to maintain to do this. So there's a simpler way to achieve this, right? We can then use a Step Function here. The Step Function can orchestrate these Lambda functions.

Thumbnail 2250

Thumbnail 2260

Thumbnail 2270

And if I open my template in Infrastructure Composer, as we saw earlier, it's not just adding infrastructure to our template that we can do here, we can actually add a Step Functions resource. So when I add this resource to this Infrastructure Composer workflow here, that actually updates my template to include the infrastructure as code for the Step Function. But what's really cool is I can now open Workflow Studio to now build my state machine right here within the IDE. I like visual, I like to see things, and especially Step Functions, where ASL can be quite complicated to read and understand. This just helps me to understand things better and see things better. Sorry, bear with me, dry throat.

Thumbnail 2310

Thumbnail 2320

Okay, so we can start building out then our order process here. So this first Lambda function, and that's going to be Submit Order. Don't worry, I'm not going to make you watch me build the entire workflow, because no one learns anything from that. And then let's say reserve the inventory. Okay. And we can go ahead and just keep dragging these different actions into our workflow to build up the steps that we want to complete.

Thumbnail 2360

Thumbnail 2370

So let's look at one I've already built so that we can see this a little bit easier. If you have the AWS add-on to VS Code or any of your other IDEs, you can actually view all of your resources within AWS right here in the IDE without having to log into the console, which is really cool. So I can see here this particular Step Function and I'm going to open that within Workflow Studio. And you're going to open the Workflow Studio. Try that again. Okay. Open Workflow Studio. Voila, there we go. That's better.

Thumbnail 2390

So this is one I built earlier, because nobody wants to see me build all of this. But what we can see now is the different steps of the workflow, all of which are Lambda functions. But we've got some error handling. So if payment fails, we can reverse the process that has just happened, so that we can release the stock that was reserved, etc.

Thumbnail 2430

And we can trigger some events so that if we've got another microservice or another bounded context, in this case, maybe we want to just take a stock check to see whether we need to order more stock now that that item's been bought. But as I mentioned, these are all Lambda functions, and we want to get rid of code. If we're going to create an order, that's really just writing something into a DynamoDB table and creating an order ID. So we can actually just search for actions here, and we can put a DynamoDB option right here. It's getting a bit small now, sorry.

Thumbnail 2460

Thumbnail 2470

Alright, okay. So we can then configure the item by clicking into the inspector, and we can configure what the step's going to be called, which table we're going to call, and so on and so forth. We can start to just replace each of our different Lambda functions as we go through. Now something like take payment, we don't want that to be a single SDK call. There's a lot of catches that we need to do and error handling we need to do within that Lambda function, so that makes sense to remain as a Lambda function. But this means that if we replace all the different Lambda functions with direct SDK calls, we've massively reduced the amount of code that we even have to manage.

Thumbnail 2540

I know that as developers and as coders, our job is to write code, but we don't always have to. Sometimes it's easier to just let these managed services do what they were designed to do and do those direct integrations. Now, SDKs aren't replacement for business logic, as you can see, and as I mentioned, something like taking payment still makes sense to have a Lambda function for that. So you still have your important logic and your important workflows written in code with all the checks and balances that come with that. But we can start offloading some of this lighter lifting to things like SDKs to be able to make our lives easier, ultimately.

Question. Yeah. Does it make sense to pull out, like if you have a DynamoDB call in that business logic, does it make sense to pull that out and have it call a Lambda after that Step Functions for that business logic? So it depends what you're doing. Ultimately, when you're using a Step Functions, it's not the DynamoDB call that's going to be calling that Lambda function afterwards. It's just an orchestration mechanism, right? So you're just saying which step happens next. That's powerful because then you've got a definitive pathway that's visual to say we know exactly what's going to happen in what situation. You can use the built-in error handling, the retry logic, without having to write that all yourself, but still have it be as reliable as it would be if you were implementing it in code. Okay.

Thumbnail 2600

Maintaining Consistent Deployments Across Multiple Accounts with Dynamic Templates

So that's code reuse. We've talked about how we can reuse code across functions. But I actually now want to take a step back away from code altogether, just a little bit. I know this is a code talk, but sometimes it's important to think of the bigger picture. In this bigger picture, what we really need to think about is our account strategy when we think about deploying our Lambda functions, and not just Lambda functions, but actually all of our infrastructure or all of our code.

How many of you deploy your workloads to one account for all of your development environments, you know, dev, test, prod? Okay, I'm preaching. Okay, I saw a hand at the back there, very shyly put up there. I'm guessing then everybody else is deploying everything to different isolated accounts. Excellent. Okay, so I'm going to preach to the choir here then.

So there are reasons to have everything in one account. If you're a small startup, it's easier to put everything in one account because it's easier to manage. If you're not deploying an awful lot, you've only got maybe one small application, you don't necessarily want to start deploying everything to multiple accounts. But there's a major risk with deploying everything to one account. The immediate one is if someone compromises your account, that's a problem because they've now compromised everything in your account, including prod. But also there's just the human element. If somebody makes a mistake, they can accidentally take down prod just because everything's in one account.

So a multi-account approach is also on the table where we split out everything into multiple accounts. But that has its complexities too, right? If you split everything into its own account, it can be more complex to manage. And again, if

we're talking startup size, maybe that's not such an issue. Maybe you've only got 10 accounts. But I work with global pharma companies, and I can tell you they've got something like 400 accounts that they're deploying things to. So we get a completely different level of complexity to manage. However, you've got isolation, and there isn't one right or wrong answer there. I personally, because of the size of customer I work with, prefer to have that account separation, but at a very minimum, make sure your prod is separated from your testing and dev accounts. So then any mistakes that happen in dev are not going to affect your prod.

So let's say we do move towards this multi-account deployment then, and we want to maintain consistent deployments. I think I've got the right room here. I don't necessarily need to explain why we need consistent deployments. I think that's really clear. But in order to have those consistent deployments across multiple environments and multiple accounts, taking into account everything we've discussed today about not wanting code duplication, we then need to start thinking about having dynamic code and then building in that dynamicness into our deployment pipelines so that we can keep our code bases manageable whilst still ensuring we're deploying the right things to the right environment. Again, I'm going to bang the drum on reducing code duplication, and so we'll look at how we can do that within our code here.

Thumbnail 2810

Thumbnail 2820

So if we go to our template, then again, you'll notice at the top we have this section called parameters. And this is really your first port of call for making your code dynamic, to make it react to various different situations. In this particular case, environment. So we can have a parameter that takes in an environment, and we can do simple things like changing a function name based upon the environment. So we can add a prefix or a suffix depending on this input. We can also do more complicated things with this. We can do lookups into maps to see whether or not the memory needs to change based on an environment, or we can even decide whether or not a resource even gets deployed. So if you're deploying into prod, you might want everything deployed, but if you're deploying it into dev, maybe you don't want some of that heavy logging and monitoring, which costs a lot of money once it starts running up into high volume.

Thumbnail 2880

Thumbnail 2900

So let's look at mappings then. So a map, and this goes not just for SAM but for CloudFormation as well, and to some extent Terraform, but it looks a little bit different in Terraform. A map is essentially just a map, a key value pair map of a key that you choose and a value you want to associate to that key. So in this particular case, I don't want conditions, but we'll leave it there because I'm going to talk about that in a minute. And if someone wants to tell me how to completely erase AI from my IDE after this talk, I would welcome that greatly.

Thumbnail 2930

Thumbnail 2940

Okay, so let's do memory. So I want to have memory set dynamically in my template based upon whether or not we're deploying to dev or we're deploying to prod. So I'm going to define a map called memory, and then I'm going to say, okay, the key of dev size. And let's make dev really nice and small, 128. And then I want to do prod again now. One day it will be useful. And then let's say we want that bigger, we want that to be 1024 when I can type correctly.

Thumbnail 2970

Thumbnail 2990

Okay, so now we have our map. So if we go into our first Lambda function, then we can easily then fetch that value by going memory size, find in map. And then that is actually a helpful suggestion, where we refer to the name of the map, we refer to that environment parameter, and then we pull out the size variable. And so when this runs at runtime, and when it runs at deployment, it's now going to put the correct value in there for the environment that we're deploying to. We don't have to have two separate code bases for prod and for dev because we can accommodate for both of them in the same template.

Now, if we want to determine whether or not a resource is even deployed depending on the environment, we can use something called conditions.

Thumbnail 3030

Thumbnail 3040

Thumbnail 3050

Thumbnail 3070

You would think it would learn. Okay, so for conditions, we're going to check is this prod. It's helping, it's helping. So we're going to say if the value of the environment variable is prod, this is going to evaluate to true. If it evaluates to true, our resource will be deployed. If it doesn't, it won't be deployed. And so what we then do is go to our Lambda function here. We add the condition, and we just refer to that condition. And so now, this resource will only get deployed if the environment equals prod. And so this means that we can make our templates really nice and variable and dynamic. We can avoid deploying things that we don't need to deploy. So when we're doing cost reduction or cost savings, we can control that all within the same template.

And the one final one I just want to cover, which is SAM specific in this template, is globals. So globals are really powerful because we can set the parameters at the top of our template and it will apply to every one of those resources. So in this particular case, we've set some global variables for our Lambda functions. So every Lambda function will inherit the timeout of 30 seconds. It's always going to be deployed with the Python runtime of 3.13. And then we're going to give all these environment variables to every single Lambda function. So we define those variables once, and they apply to every function within the template. Really, really powerful. And as I say, this is specific to SAM. You won't find this in CloudFormation.

Thumbnail 3150

Thumbnail 3160

Thumbnail 3170

Now, I'm going to finish up quickly by just going a little bit further into how we then use this dynamic nature when we're actually using deployment pipelines. So I like GitHub Actions, it's one of the favorite workflows that I like for deployments. And when we're thinking about deploying our workloads, what we want to really achieve is consistent deployments. And so we can deploy these dynamic templates using variable files or variable overrides using some workflow definitions here. So this is what a GitHub Actions workflow looks like. Essentially in here, we define when we deploy, what permissions we deploy with, and how we're going to deploy. So in this case, we're obviously using SAM. And we pass in the credentials. So in this case, I've created an OIDC provider within my AWS account. And then we give the instructions for how to deploy. And as you can see here, we do a variable override to set the dev environment.

Thumbnail 3190

Now this workflow is just deploying to dev, but we could have this workflow deploy to prod. And what we can also introduce is a human review gateway. So we can do that deployment to dev, and the prod deployment won't even begin until someone's reviewed that dev deployment and made sure it actually went correctly. Sometimes with GitHub Actions, it'll say it's deployed correctly, but actually when you inspect it a little bit more, it hasn't actually deployed. It's just gone, yes, the action completed correctly, which isn't necessarily always useful. So sometimes having that prod human gateway is really, really fantastic.

Thumbnail 3220

Thumbnail 3230

Thumbnail 3250

Thumbnail 3260

Thumbnail 3270

And then when you actually go into, there we go. So when we go into GitHub Actions, this is what the workflow looks like when it's run. So these were a few when I was setting up the demo. Obviously, setting up credentials is often difficult. And so I had a few failed deployments. But every single one of these you click into, you can then see a full, you can see the workflow. But you can also see every action that took place. So you've got that full audit trail as well of how it deployed, whether it encountered any issues, and for each one of those steps. If we go to the SAM build and deploy, that same output you see in the console, you'll see here in the logging. So you're always able to check what's happened.

Thumbnail 3280

Building Scalable and Maintainable Serverless Platforms: Key Takeaways

Okay. So you came into this talk and we presented you with a common enterprise challenge. You had lots of isolated Lambda functions, you'd migrated from a monolith, split them out, and then realized that you had complexity. So let's recap what we've actually covered. So in the first part, we covered organizing our functions.

We talked about whether or not to have single or multi-purpose Lambda functions and the context within which that would be better, and how we use bounded context to start merging functionality together. We then talked about code reuse and how we can use Lambda layers to reuse code across our functions, minimize that duplication across our code bases, and if we want to go really radical, remove code altogether using SDK integrations. Finally, we talked about consistent deployments using dynamic code and deployment pipelines.

So the key message I want you to take away here is that these points that we're talking about, it's not just about deploying Lambdas. It's about making sure that you're building scalable and maintainable platforms that can grow with your enterprise needs. So if I want you to do anything after this talk, it's to go away, pick one pain point, and then start applying these principles that we've taught you today and see what happens.

Thumbnail 3390

And if you want to talk to us on LinkedIn about anything after, you know, obviously you're not going to experiment now, but if you do go away and experiment and you actually want to talk to us about it, reach out to us. We're accessible and you can find us on LinkedIn. There are other sessions, we've picked these sessions specifically because you should still be able to make them. So these are our recommendations for sessions to go and look at and watch.

Thumbnail 3400

Thumbnail 3410

And we also have some other resources here. So if you scan the QR code, this will take you to a lot of serverless learning if you want to further your journey. And with that, I would like to thank you all, on behalf of both myself and Lefteris. Thank you.


; This article is entirely auto-generated using Amazon Bedrock.

Top comments (0)