This content originally appeared on Bits and Pieces - Medium and was authored by Ashan Fernando
Different techniques to share code between serverless functions
Today, almost all the popular cloud service providers support serverless functions. Some popular ones are AWS Lambda, Azure Functions, Google Cloud Functions, Cloud Flare Functions, etc. Over the years, these functions have evolved from running utility code to full-fledged applications.
However, one common challenge across all the serverless function platforms is its inherent limitation on sharing code between functions. Though several cloud providers introduced novel approaches to share code (e.g., AWS Lambda Layers), most of these techniques are application-specific and may be limited to general-purpose sharing. Therefore, many create packages (e.g., NPM) to share code between functions.
But are there any special techniques, tools, or platforms to improve code sharing? And, what are the significant limitations we need to address when sharing code between functions? Let’s find out.
Why Sharing Code Between Functions is Difficult
Serverless functions, by nature, are distributed systems in runtime. Though this is useful for scaling and functional isolation, it creates challenges when sharing code.
Suppose we need to use a date-time utility code between two serverless functions. And, if we set up the code within a single code base, including the two functions and date-time utility code, for any change made to the date-time utility code, we need to test both functions and deploy them.
project-root/
│
├── date-utils/ # Shared date-time utility code
│ └── date-utils.js # Main file exporting utility functions
│
├── function1/ # Serverless function 1
│ └── handler.js # Main handler for function 1 using date-utils
│
└── function2/ # Serverless function 2
└── handler.js # Main handler for function 2 using date-utils
Doing it for two functions may seem relatively straightforward. But, when the number of functions and shared code increases, it becomes difficult to keep track of the dependency graph of each function. Also, having to update all the functions when a shared code changes defies its purpose.
The main bottleneck here is that, each fucntion depends a specific portion of the code in the file system.
Sharing Code using Packages has a Major Limitation
That’s why sharing code using packages has become popular for serverless functions. With packages, each function can depend on a particular version of the shared code. Yet, packages introduce two significant problems.
- Each package has its lifecycle, and testing them with functions becomes complex.
- Tracking the dependency graph of functions → packages and packages → packages and their respective versions is a challenge.
Therefore, when the functions and their dependencies grow in numbers, making dependencies into packages doesn’t scale well. Even if aggregating more functionality into a package limits their number, it makes it difficult to track the impact when we modify them since different functions depend on different sets of functionalities.
However, newly added features like NPM workspaces somewhat reduce complexity; a fundamental approach is required to share code between functions.
Introducing Bit for Serverless Functions
In the world of Bit, the fundamental code unit is the component. These components can be put together to build complex applications.
This is achieved by the Bit platform and its tooling by encapsulating the component code, versioning it, managing the component dependencies, and detecting component changes.
And do these features sound familiar to you? These are the exact problems we need to address when sharing code between serverless functions.
Bit Support for Different Serverless Functions and IaC Frameworks
Bit already supports several serverless function types. These include;
- AWS Lambda (Direct) — Demo Scope
- Serverless Functions using Pulumi (Supports AWS Lambda, Microsoft Azure Functions, Google Cloud Functions) — Demo Scope for AWS Lambda
- Cloudflare Workers — Demo Scope
Bit implements App types, Envs, and Generators (Part of Env) to support the deployment, local emulation, and testing of Serverless functions or the IaC framework.
- App types — Handles the deployment of the Serverless function
- Envs — Handles bundling, lining, etc., of the Serverless function
- Generators — Uses templates to create the skeleton of the function
With this support, we can directly create serverless functions that support different cloud providers. Following is an example of an AWS Lambda function created as a Bit component that uses the Pulumi IaC framework.
import { APIGatewayProxyHandler } from 'aws-lambda';
/*
Below, dateUtil is a dependent component (reusable piece of code)
*/
import { dateUtil } from '@bit-pulumi-lambda/demo.utils.date-util'
/*
Lambda function handler
*/
export const handler: APIGatewayProxyHandler = async () => {
const dateType = process.env.DATE_TYPE || "";
const message = `Hey!, ${dateType} is ${dateUtil(dateType)}`;
return {
statusCode: 200,
body: JSON.stringify({ message }),
};
};
Here, you can see that the function usesdate-util as a dependency tracked as a separate component (not just another NPM package) in the Bit platform.
Code Sharing using Components Between Functions
Components can play a significant role at scale when it comes to code sharing. Let’s look at an example where date-util component is used by two Lambda functions. Below is the dependency graph tracked in the Bit Platform.
Suppose we want to modify the date-util function code. If we don’t use components and all the code in a monorepo, we need to manually find out the dependent functions, test them with modified code, and finally, deploy both functions to AWS.
One unique benefit of this approach is that since all the functions and dependent code are colocated in the same Git repo, it's easier to modify and verify the changes.
With Bit workspaces, you can gain the same benefit even if your functions are spread across multiple applications, where you can import all the required components to your workspace and work with them as if they are in the same repository colocated together.
Bit workspace/
│
├── component-date-utils/ # Shared date-time utility component
│ └── date-utils.js # Main file exporting utility functions
│
├── component-function1/ # Serverless function component 1
│ └── handler.js # Main handler for function 1 using date-utils
│
└── component-function2/ # Serverless function component 2
└── handler.js # Main handler for function 2 using date-utils
Overall, this creates a unique development experience for working with serverless function components and shared components.
CI/CD Support for Function with Ripple CI
The next advantage of using components for serverless functions is that it's easier to set up a CI/CD pipeline that can detect the modified components and their dependencies and finally release the functions that were affected.
Following is an example of the build that is being triggered after modifying the count-lambda function using Ripple CI.
Since the unit tests fail for the function, Ripple CI stops releasing the count-lambda function to AWS. One of the main benefits here is that once the error is fixed, Ripple CI can continue from that function, building the affected components in the dependency graph.
Learn More
- AWS Lambda Development at Scale: Using Composable Architecture
- How To Effectively Share Code Between AWS Lambda Functions?
- Composable Cloudflare Architecture: A Guide
- Building a Serverless Architecture with AWS Lambda and API Gateway
How to Share Code Between Serverless Functions was originally published in Bits and Pieces on Medium, where people are continuing the conversation by highlighting and responding to this story.
This content originally appeared on Bits and Pieces - Medium and was authored by Ashan Fernando
Ashan Fernando | Sciencx (2024-09-30T19:56:54+00:00) How to Share Code Between Serverless Functions. Retrieved from https://www.scien.cx/2024/09/30/how-to-share-code-between-serverless-functions/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.