Serverless Then & Now

Muthu Venkatachalam
The Nobody’s of Tech
9 min readMay 15, 2022

--

Serverless has gained lot of traction over the last few years and I too have traveled this trend shift:

(Cloud Native) Microservices → (Vendor Locked) Serverless

Doesn’t really look very appealing when you define it like that, does it? :) So we will call it ‘Serverless’ itself. I was too worried about this leap as well when I first came across Serverless and how am I going to get these gains from Microservices Architecture (MSA) all squared up in the Serverless space. I did try it as well, only to realize later that ‘that isn’t how it works’.

This post is more of sharing my personal experience with Serverless and highlights the change in thought process on various things that matter to us as ‘Enterprise Engineers’ from when I started in ‘Serverless’ to where I’m today after 2 years in Serverless. Here are my thoughts and how my stance has changed with more time spent in Serverless:

Vendor Lock-In → Okay, So?

‘Vendor Lock-In’, this is the very first block for many of us moving from Containers to Serverless. Containers are cloud-native, great! The same container can run in your local docker, AWS EKS and Azure AKS.

But, Wait a Minute :), only the container is Cloud Native, I repeat, ‘only the container is cloud-native’ — yes just that the final deployment unit. How about the Kubernetes cluster itself ? AWS EKS and AKS are still locked to the respective cloud vendor, right from the bare metal instances, networking, config-maps, secrets, encryption and RBAC policies tied to the Pods, everything is subject to the Vendor (Hosting Cloud Platform)

Cloud Native is great, but once you step on to a vendor managed cloud outside your data-center, it’s Vendor Lock-In all the way. So, why do more people talk about vendor lock-in when it comes to Serverless but not Containers ;) ‘The Enterprise DevOps magic/myth’ — Unlike Serverless, Containers aren’t managed by developers at an Enterprise for the most part, so the pain isn’t as widely felt across the development teams. In software terms — it all just abstracted. So don’t take Vendor Lock-In by face value and over-engineer your Serverless applications:

  1. Follow the best practices and adopt Serverless for what it is worth — Simplicity, Ease of Management & lower TCO
  2. There are plenty of things you need to focus on when it comes to Serverless Engineering, Vendor Lock-In is a non-issue, don’t waste your time solving an issue that doesn’t exist!

Framework → Library/Utility

Framework(s) are closely related to the Microservices development, we can go one step further and say frameworks make Microservices a breeze, some of the most popular ones are — Express, Spring-Boot, FastAPI. They provide a lot of out-of-the-box capabilities, provide best-in class implementations for rest-api(s), database and other integrations. Not to mention, the extensive documentation and good community backing for all of them.

But, when it comes to Serverless, is the framework really your friend? This stackoverflow thread explains the fundamental difference between framework and library really well

The key difference between a library and a framework is “Inversion of Control”. When you call a method from a library, you are in control. But with a framework, the control is inverted: the framework calls you

This whole notion of moving away from frameworks is really hard a hard decision to make as a developer / engineer. Why would you call something ‘best in class’ but caution not to use it for Serverless development. I was outraged to see this article on not to use expressjs on lambda when it came out (I even clapped for the first comment that said ‘I disagree with the author 100%’ ). Also I’m someone who previously wrote an article about using NestJs in AWS lambda, I’m here more than a year later saying, ‘try not to do it’.

The main reason is in the answer from stack-overflow — `When you call a method from a library, you are in control`. This is a very important tenant when it comes to Serverless. Unlike containers or VM(s), Serverless offerings like Lambda(s) aren’t a glass box, there is a lot of abstraction going on to provide us with the ease of working on press of a button.

Adding more abstraction & layers by means of framework only moves you further away from attaining Serverless Nirvana :)

Also, here is my personal experience with running NestJs in AWS lambda, when I started getting into Serverless, this post helped me get there and we started facing the ‘unknown error’ that occurred rarely in production, I say unknown because I still haven’t found the root cause for it.

my comment on the post, and someone else confirming the same

So what is the alternative then? Use Libraries, period. There are plenty of libraries that help you get there and make use of Serverless without altering its very nature. If you need more than what the libraries offer, then extend the library, try to avoid writing your own :), Here are some of the alternates I prefer:

  1. Lambda Powertools — Python, Typescript — These have been a great addition to the Serverless development. (we extend these core-libraries to implement our own middlewares, logger decorators on top of powertools)
  2. Jeremy Daly’s — Lambda-API and it’s typescript extension ts-lambda-api are things we use as well (these are little outdated though)
  3. Other note-worthy libraries that I make use of are — dynamodb-onetable, typebridge, also the modular & typed aws-sdk-v3

(On top of these, we use the standard & popular client libraries available for elasticsearch, relational databases, axios, requests, etc.)

Microservices → Modular Monolith (Hexagon)

This is also a walk back from my initial Serverless days, not a walk of shame but a walk towards betterment.

The ultimate challenge I had with Serverless is finding the right balance. Should it be more granular like a single-purpose function like most pundits prescribe, or should it be a monolith (Lambdalith)? ‘Monolith’ may sound like a cuss word to people who did all the hard work moving to Microservices. But it’s not as bad as it sounds though in the Serverless world.

As any good good MSA Samaritan would do, I too opted for ‘Independent Microservice’:

  1. 1 lambda for all the API(s) together in
  2. 1 lambda for all the Event Driven workloads
  3. 1 lambda for any stream events like dynamodb streams, etc.

Problems started kicking in when the project expanded and every sub-domain or entity we modeled, had 3 projects within itself and duplicated everything from models, data-models, repositories, etc. Once I hit this snag, it felt like some piece was missing.

While we were trying to find the missing piece to the puzzle, there were several things that helped me solve this and eventually lead us to adaptation of Hexagon (Ports & Adapters) for Serverless.

A Deep Dive on DDD, this AWS Blog and other articles & videos from Luca Mezzalira around Hexagonal Architecture for Lambda. This AWS sample repo by Heitor Lessa also helped with the hexagon implementation. A lot of poking around at the Serverless community in twitter do design a holistic Hexagon for Serverless

The general blueprint for Serverless Hex is the same. But there are a lot of focus areas and scope of improvements to tailor fit to your needs (I plan on writing a separate article with the e2e hexagon implementation with more concrete coding examples)

  • Make all your dependencies as Dev-dependencies, don’t package them with the code when you build
  • Define lambda(s) with multiple triggers and their respective external adapters, in one source Repo per Bounded-Context (DDD)
  • Use Dependency Injection principle to selectively inject the dependent adapters (lambda1 → dynamodb, lambda2 → s3, lambda3 → EventBridge)
  • I also created a custom layers utility to build the necessary layers and make it available in the AWS account
  • The robust deployment config is the important component that brings them together (similar to the Serverless framework or the SAM Template), linking the lambdas with the layers and working out the magic at runtime :)

I plan to OpenSource both the Deployment Utility utilizing the Robust-Deployment Config & the Layers Utility. Both Serverless Framework and SAM Template, uses AWS CloudFormation , so we opted to use the SDK and create a tailored library/utility around it .

Development Team → Engineering Team

When teams adopt Serverless development, a lot of the team’s responsibilities are delegated to the cloud vendor, and there is an inflow of new responsibilities that the development teams are becoming responsible for.

What responsibilities go away?

  • Serverless comes with a lot of out-of-box features like the integrated event-driven components, database streams, protocol free integrations
  • Lower TCO, Fail-Fast model, shorter path to prod, scales well, multi-tenancy all along
  • Orchestration & Choreography becomes much easier through services like step functions (when compared to Microservices)

Newer Responsibilities with Serverless

  • Developing in Cloud becomes a huge shift from traditional development. Many of the event-driven handlers and stream components cannot be efficiently developed / tested in local environment
  • Teams as they move on to do advanced Serverless, need to understand and navigate things like cold-start, provisioned concurrency, Queue timeouts, Queue Batch failures, Failure Handling with Events, etc.
  • With Serverless, development teams become responsible for the Security & RBAC components like IAM roles & policies which is relatively new to teams coming off of a Microservices background
  • Most Enterprise teams build Serverless components that integrate with many On-Prem systems, which brings Networking (VPC, Subnets, Security-Group & ENIs) into play
  • Development teams need to take up most of the Serverless Devops activities or work more closely with devops team as most of the Serverless build or deploy needs are not well understood by external teams
  • More & More components are becoming Serverless — Redshift Serverless, Aurora Serverless, Sagemaker Serverless, All traditional batch processes are moved into Step Functions

The shift clearly shows that the coding activities of development teams have reduced as they do more Serverless. While it has resulted in bringing more ownership, software lifecycle management, security, networking & DevOps traits into the Team’s must have skills.

Hence there is a definite need for Development Teams to re-calibrate and become an Engineering Team, who must oversee the system as whole using engineering principles (they aren’t responsible only to create functional software anymore)

References

Conclusion / TL;DR

  • While the fundamentals of the Software Engineering principles and Microservices still hold good, the entire Serverless Development Model should be viewed with an Engineering Lens.
  • This post sheds light on some of the important steps towards adopting Serverless, there are many other focus areas also like — Observability, Performance, Cost, Scalability, Event-Driven-Architecture, etc. Priorities would vary depending on the nature of your work & engineering team.
  • While the traditionally ‘harder to implement by a small team’ infrastructure is becoming easier with the help of the Serverless movement, it is only fair that the teams shift their focus towards Engineering for managing them efficiently.
  • I have had more success with Serverless once I learnt from my experiences and went back to the drawing board to remap my original thought process from when I started the Serverless Journey. This is more of a rinse-and-repeat exercise, I plan on doing every few months for continued success with Serverless.

Follow-Up

I plan to share the sample Serverless Hexagonal Development pattern/model along with the deployment and layers utility in my next post

--

--