A couple of things about serverless development (not a rant)
Update: you can now read this post also on my new website (Ad free, cookie free, tracking free…).
I am not sure I really like “serverless” software development.
I mean, I use it, I get it, I appreciate a lot of aspects of it but — do I *like* it? Is there any fun in developing serverless applications or are we maybe doing it just because is the right thing to do and we should suck it up?
My experience is currently limited to AWS Lambda, so what I am saying here may or may not apply to other serverless platform as well. AWS is embracing Lambda to the full extent: spinning up an EC2 instance, nowadays feels passé, expensive, uselessly convoluted. A server to maintain? A shell? Installing things? Oh god. No. Please, no.
I am developing Lambda since several months and along the way I found myself in an infinite roller-coaster of “Ah-ha!” moments followed by frustrations about performance, limitations, constraints and then another announcement which lifted some of those limitations followed by another “Ah-ha!”, and so on and so forth.
Serverless is still a frontier, and it’s not clear if this is a good or a bad thing, to be honest: if you know me, you know that I am not scared of the latest and greatest. There is really not a “framework” to help you with some boiler-plating, nor some guidelines on how to name things, or where to put them, or what to avoid (if not just for a matter of costs, but not design or best practices). If you are looking for answers, you’ll probably end up with even more questions. But it is a frontier where you want to be at, nonetheless. Serverless is here to stay and evolves rapidly, because this is the way; it’s only the implementation and toolchain that need (a lot of) help.
Composing serverless functions in services is confusing, at best: you read about those “serverless microservices”; but how “big” is a serverless microservice? What is it made of? Is it just a bunch of a Lambda functions sharing a scope, a domain, a Dynamodb table, maybe? And how they communicate with each other? Not in a sync way, god forbid, but just reacting to events (sources); but an event can also be an invocation from an API Gateway, which is synchronous invocation after all. So maybe I *can* actually contact another microservice (another Lambda) by using a private API Gateway; or maybe I may call it directly, knowing its function name by using service discovery? Options, options everywhere…
Now you need to use some sort of persistency layer; a PostgreSQL database for example, except you really can’t, or shouldn’t or it depends if you want to put another service in the middle (AWS RDS Proxy) or not. Because serverless means “stateless” and stateless means you can’t use any sort of connection pooling which in turns means terrible performances. OK, let’s go DynamoDB then–where you realise that in modelling your data you have to think exactly in the opposite way you did with your beloved RDBMS. You started with the need of serving a simple POST request and now you are digging into a hole filled with GSI, sparse indexes and attribute overloading. Ripple effects, ripple effects everywhere…
The minimalism at the core of the serverless idea creates a paradox: can you really go ahead and create a couple of services that are not trivial and without having some kind of structure, a common set of libraries, a standard way to log information and manage errors? Not really and then, here you are writing your own “framework”, while being very aware to not wanting to *actually* write a framework for real since you still want to embrace that sweet minimalism, the strength of convention over configuration and libraries. But the risk of not using a consolidated set of components, is long rounds of refactoring, over and over, hence the paradox.
Don’t get me wrong (as for the title, this is not a rant), Lambda is incredibly powerful: effortlessly and infinitely scalable, event sourced and “async first” by nature, reliable (one single lambda which fails cannot drag an entire service down), almost language agnostic, pay-as-you-go, you name it. As I said at the beginning, the idea of firing up a server, installing, running and updating things, tinkering with its scaling model and then paying for it even if you don’t use it starts to feel really old and tired — including maybe containers.
But serverless falls short in a super important aspect of software development: the joy of developing on your local machine, with a fast feedback loop–simple as that. Fully local development of even a relatively simple service is almost impossible without a lot of instrumentations, hacks, tradeoffs. This is a big step backward, which may be frustrating but the future is not hopeless!
Localstack could save the day, but I haven’t fully tried it yet; several of its plugins are not free and the serverless plugin uses Docker which in macOS can be really slow. I have also been told that feature parity with AWS is hard to keep up with.
AWS SAM cli was my first choice when I was looking for something to help developing with a fast feedback loop (save, test, fix, save, test…); almost there, but not quite; it is also using Docker which means that every request to the local http service take seconds. There is also not feature parity with real AWS services in terms of validation, which means that you may end up trying to deploy something that in AWS just doesn’t work.
Then there is serverless. I haven’t used it because I feel like not wanting another abstraction to work with; more concepts to grasp, more yaml to write… We are in AWS and I prefer using CDK for now. Cloud agnosticism is a problem that I decided not to consider at the moment.
Then I found Serverless Stack Toolkit (not related to serverless) and this one takes the cake, for now. Using a super clever hack involving a websocket proxy, sst allows you to develop on your local machine while at the same time the execution of your lambda is ghosted on AWS (for real), so you can actually contact an use the services in there (if you are in a VPC things are a little bit more complicated, but it’s covered in the documentation). You still need an internet connection to work, but at least the feedback loop is almost instantaneous. Sst supports TypeScript, uses CDK at its core (initially with a fork, but since version 0.10.0, with the original one), and bundles everything with the amazing
esbuild out of the box. Not only it is very well documented, they also have a guide to help building a full serverless application using React for the frontend and Cognito for authentication, which is by itself probably the best tech writing you have read in a long time. Trust me on this.
In conclusion, I will definitely keep on using serverless while actively trying to improve its development experience by offering documentation, patches to open source projects, and advices. It feels a bit like nodejs 10 or more years ago, when we all knew it was about to change everything but man, wasn’t it hard to do something decent with it? :)
By the way, I just released a simple serverless project using sst. It’s called Urlino and can be useful if your company needs an internal URL shortener, for example.
Thank you for reading and follow me on Twitter for more technicalities.