5 Things To Consider For Writing A Lambda Function

A few years ago, Amazon Web Services (AWS) launched it’s new service AWS Lambda. Since its start the service is generating more and more interest for the whole world of serverless computing. I’ve also started using it this year and today I want to share 5 things with you what I think is important when developing such functions.

1. Think about the use case

Lambdas are a good fit for event processing which is not required to have a super low latency. If you have to process a stream or would like to build a fancy workflow (maybe using Step Functions?), Lambdas are the way to go! They are small and can do one certain step of a process. And the good thing is you don’t have to take care of the provisioning. But compared to using it as a backend for an API, you should consider another solution. This is due to the cold start of a Lambda which can take a few milliseconds (e.g. for small sized NodeJS functions) or up to a few seconds (especially Java functions need more time to start because of the JVM). If your API has effects on the user experience (e.g. you want to load some data asynchronously), it’s might be worth to evaluate other solutions.

2. Choose your language

It’s important to think about this before you start developing your function. Generally, I’d suggest writing your functions using NodeJS, because the packages are mostly very small (see also point 5) and this improves the startup time. There is also a high support of NodeJS libraries out there to support your development. But there might be some cases where you have to use e.g. Java. One use case might be that you are migrating an existing application to AWS Lambda and this app is already written in Java. Then it makes sense to reuse existing code in order to avoid new bugs in your business logic. But again, consider point one: using Java makes the most sense if your Lambdas are working in the background without expecting a low latency. Here are some small starter snippets for NodeJS and Java:

NodeJS

module.exports.handler = function(event, context, callback) {
// use event to access event data, like from S3, Api Gateway or similar
// use callback(null, { }); to send a "positive" response
// use callback('error') to send an error response
}

Java

import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestStreamHandler;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;


public class AbstractRequestStreamHandler implements RequestStreamHandler {

    @Override
    public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context) throws IOException {
        // use inputStream to read the event data
        // use outputStream to write some response data
    }
}

Personally, I prefer to use the RequestStreamHandler interface, because you are more flexible to use your own processing of the event data. This comes in handy if you want to do some custom format mapping. But that’s up to you!

3. Use a CloudFormation template

This point is the most important one in my opinion: always use code to describe your infrastructure! It ensures that you can work on the same infrastructure with different team members, because you always get the same result. This is really valuable! Though it was quite complicated to setup a Lambda in the beginning using CloudFormation, AWS has improved this a lot by publishing the Serverless Application Model. With this type, it’s easier to declare a function and map it to a supported event. Here is an example:

SampleLambda:
  Type: AWS::Serverless::Function
  Properties:
    Handler: index.handler
    Runtime: nodejs6.10
    CodeUri: target
    Policies:
      - AWSLambdaBasicExecutionRole
    Environment:
      Variables:
        SOME_VAR: "My nice environment variable"
    Events:
      GetResource:
        Type: Api
        Properties:
          Path: /api/hello
          Method: get

In this example a Lambda function gets triggered if a GET request is sent to “/api/hello”. It’s using NodeJS 6.10 and also gets an environment variable injected. Environment variables are a really nice feature to reference other resources like AWS SQS, S3, etc.; Another important point is the Policies section which is described in the next section. To look up all possible properties, take a look at the model reference: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md One last advice: allocate enough memory if you’re using Java functions.

4. Attach policies to your Lambda

AWS has a really strict access management. This is good, because it ensure that you can’t access something if you don’t have the right permissions. But it can get a little bit annoying if you’re deploying a huge stack, start testing your system and your logs show you that you’re not allowed to access a certain resource. So, keep an eye on your policies when accessing other resources. You can choose by using managed policies (like you’ve seen in the code example above which give your Lambda the ability to put your logs to a CloudWatch log stream) or write your own policies. This is how it looks like if you want to access AWS SQS for example:

Policies:
  - Version: '2012-10-17'
    Statement:
      - Effect: "Allow"
        Action:
          - "sqs:DeleteMessage"
          - "sqs:ReceiveMessage"
          - "sqs:SendMessage"
        Resource:
          - !GetAtt [MyQueue, Arn]

It’s giving access to delete, receive or send a message to the referenced queue called MyQueue. If you’re wondering what kind of actions are provided by AWS, you can look them up here.

5. Reduce your dependencies

Last but not least, it’s very good if you can reduce your dependencies as much as possible. As already said above, Lambdas have a cold start which can take up to a few seconds for Java functions and reducing the dependencies can improve this start time a lot. As an example for Java: in server applications I tend to simply include libraries like commons-lang or Guava, because they have some great helper classes and methods which make my life easier. And – of course – I don’t have to reinvent the wheel. The problem is now that if you’re just using one class of a dependency with a size of 500 kb and you have 4 more similar dependencies, you already have blown up your Lambda function of about 2.5MB. Just think about this if you really need to include a whole dependency for just using StringUtils.notBlank() from it (this is of course an extreme example, but I’m sure there will be someone who has done it). NodeJS has a huge advantage in this case: they have great libraries like webpack which are able to identify the real usages of your code and filter out all unnecessary code which reduces the output a lot.