From monoliths to microservices and containers to serverless functions: the software engineering world is changing fast. Popular technologies from today will be outdated tomorrow and it isn’t easy to follow them all. The same is true for taking the first step when going serverless. Hence I’ll present you with my best practices for going serverless to save you time on choosing the right way.
With this blog post, I’m focusing on serverless functions like AWS Lambda. It’s part of a series of two posts:
- Why “Going Serverless” and how to start (this)
- Best practices for your architecture and development (next blog post)
People often ask me why I’d prefer serverless functions compared to typical setups using containers or alike. For me there is one simple answer: In a perfect world, I don’t want to care about anything in my infrastructure. I don’t want to know the operating system, which security updates it needs and which packages I have to install. It should just work. The machines are handling it for me. I just need to know how it works in general and how I can use it. But not in detail. Other people who are more interested or knowledgable on this topic should take care of it. I want to develop my applications and provide a benefit for my customers. This is probably true for most users of serverless functions and the main reason why they adopt it. There are also more reasons like quick code updates (yes, updating your function’s code can be very quick) and high scalability.
Besides the general infrastructure topic, also the choice of my application’s frameworks change. Considering a fast startup of your functions, you can’t include all the dependencies you want (will be discussed in more detail in the next blog post). For example, if you’re used to the Spring Framework in Java, you usually include a lot of its modules to get Dependency Injection, Authentication, and more. This blows up the total size of your JAR file. Not only is this bad for your Lambda function’s performance. Also, in the era of cloud services, you usually don’t need to build this on your own. The key is to use managed services. These can be offered by your cloud provider or independent SaaS products. (This often counts as “Serverless” as well) As a result, you’ll also focus more on your own business logic instead of your infrastructure.
How to Start
Now that you know why you want to use serverless functions, you need to know the best ways for going serverless. As always, there are multiple ways to do that. First, you should know which serverless offerings are available. Here is an (incomplete) list:
- AWS Lambda – the first serverless service
- Google Cloud Functions
- Microsoft Azure Functions
- CloudFlare Workers – similar, but only available at CloudFlare’s CDNs and has different limitations
- more: Apache OpenWhisk, fn project
All service offerings are similar to a certain extent. As mentioned earlier, I’ll focus my blog post on AWS Lambda as this is the service I’m the most comfortable with.
Infrastructure as Code
Regardless of which provider you choose, there is one important aspect you need to consider when going serverless: you must be able to describe your infrastructure as code (IoC). This is crucial to keep track of all the serverless functions (and their versions) which you’ll create. Otherwise, I bet you’ll lose the overview in your system. Moreover, by using IoC you’ll be able to use an automated deployment & delivery process to ship your code faster and with fewer errors.
Popular choices for such frameworks are Serverless.com or Serverless Application Model (SAM). Both have a strong focus on serverless functions, with the only difference that SAM can only be used for AWS whereas Serverless.com works for multiple cloud providers. As an alternative, you can consider using Terraform as well. With all frameworks, you will have one or multiple JSON/YAML files describing the resources of your application stack. For instance, a database, your functions, log services, intermediary services like queues, and more. A sample definition of a serverless function using SAM looks like this:
MyServerlessFunction: Type: AWS::Serverless::Function Properties: Handler: com.example.MyServerlessFunction Runtime: java8 CodeUri: target/my-function.jar MemorySize: 256
You can find a lot of ready-to-use examples for AWS Lambda and the SAM framework in my GitHub repositories aws-lambda-boilerplate and aws-cloudformation-templates.
Sign up for my newsletter:
Another important aspect when going serverless is to think about the way you want to deploy & deliver your function’s code. In the best case, you can automate the whole deployment pipeline. You can choose tools like Bitbucket Pipelines, AWS CodeDeploy with AWS CodePipeline, and others to support you with that. (And I strongly encourage you to use them!) Let’s consider an example for Bitbucket Pipelines:
image: openjdk:8 pipelines: default: - step: name: Build, test, deploy app script: - mvn package - mvn test - ./deploy-app.sh
This file configures when your pipeline runs (for each commit on every branch) and what is executed (package your code and deploy it). As you can see, the definition is really small and simple to understand. However, you can further customize it to your needs. For instance, if you use different branches like
master and other feature branches.
Similarly, it’s not only important what you deploy, but also how. Especially Lambda functions can be a bit tricky here. By default, as soon as you update a Lambda function’s code, AWS Lambda will immediately take this code for all upcoming requests made to your function. On the one hand, this means you’ll get a very quick code update. On the other hand, if you introduce a bug, all of your requests will be processed with this bug in your code. Therefore it makes sense to use canary releases. It lets you partially increase the traffic to a new version of your Lambda function without immediately exposing the new code to all users. Both the Serverless framework and SAM support you here.
In conclusion, going serverless can make a lot of fun. No more messing around with low-level details, but instead focusing on the application. However, you still need to know a few details when and how to use serverless in the best way. I have provided you with a rather high-level introduction to that. The next blog post will focus on a lot more details from a developer’s perspective. For example, how to write performant Lambda functions, how to combine them in an event-driven world and which other tools or services you can use. In the meantime, I can encourage you to check out this talk about taking serverless to the next level by Danilo Poccia.