The Serverless Trilemma is a framework developed by researchers in IBM a couple of years ago (full academic paper). It helps us evaluate the quality of architectural designs and make better decisions by understanding the trade-offs involved.
This is an introductory article, we'll publish more hands-on guidance on how to apply these principles in next articles on this series. To stay tuned, please subscribe here.
There are three desired properties we should look for in a serverless architecture:
The Black Box principle indicates that each function should work
independently from the rest of the system and no implementation details should be leaked.
Consider two functions running on AWS Lambda:
Function A uses the AWS SDK to invoke
What happens if we need to move the
Function B code to another infrastructure? Perhaps the AWS Lambda memory limits aren't enough anymore.
The AWS SDK invocation process will not work anymore. In order to migrate
Function B, a concomitant modification is required in
This type of architecture makes it difficult to introduce changes and increases the likelihood of unintended consequences.
The problem here is that
Function B is leaking its implementation details, thus not working as a Black-box. The leakage is having
Function A aware that
Function B is deployed in AWS Lambda.
To satisfy the Black Box principle, we could create an HTTP API. By routing requests through the API, we can decouple the functions. Later we can just update the API routing to the new infrastructure when migrating Function B.
This simplifies change, which can be extremely valuable as the system and development team grows.
This principle is relatively simple: two functions should not run at the same time while one is in idle state waiting for the other.
In Serverless, we pay for the function execution time. Consider
Function A starts processing and being billed. At some point, it invokes
Function B and waits for a response.
Problem is: While
B is processing,
A continues to be charged, despite being idle.
Function B processing time is effectively being charged twice, which is arguably undesirable.
Substitution is about having a composition of functions behaving just like any normal function.
But what is composition?
Consider a user requests an invoice from a billing system, which is served by a serverless function. This function may rely on a second one to apply any discounts available to this user. Yet another function is responsible for calculating Sales Tax, for instance.
All three functions are composed to work as a group, hence the name "composition".
The substitution principle indicates that all three functions
together should behave like a function. From invocation methods to timeout to dead-letter-queue, everything behaves as if they were a single function.
The requester doesn't actually know this is a composition of functions. Any developer can rely on this composition without worrying about how it behaves, they just apply everything they already know.
This property improves productivity and overall quality by reducing errors and unintended consequences.
Most of the time, it is not possible to have all the properties together. Applying the Black box principle is usually easy. When it comes to the other two, there is usually a trade-off.
When we prioritize one, such as Substitution it becomes difficult to apply Double-billing.
The Serverless Trilemma framework helps to make architectural decisions
and identify what needs to be prioritized:
- Black Box improves maintainability, scalability, and resilience by decoupling components
- Double-billing enables cost-efficiency and reduces the overall concurrency level of the system
- Substitution enables code reusability and improves the quality
What should be prioritized will really depend on each case. We'll dive into more details about this decision-making process in the next articles coming on this series.
To stay tuned, subscribe here for free to receive announcements when we publish again.