Serverless is the future, no doubts about that, and I love the model. For a small agency like Ingenious it helps us reduce costs and allow us to forget about infrastructure.
As cool as serverless is, I found myself always needing to go an extra mile to deploy a complete solution, and it's not for the lack of tools. I have come to the conclusion that the problems I'm tasked to solve are tricky to get right using a serverless approach. Here's my take on why not serverles-all-the-things.
First, let's scope what we say when we speak about serverless. A definition I like -that may be incomplete- is the following: "Serverless is the ability to scale up, but also to scale down to 0".
I would add that typically serverless comes in the flavor of specialized services. For example, if you do a serverless app to store and retrieve things from a database, you may need at least functions, datastore, and authentication services. Probably also some background job processing, CDNs, etc.
In theory, this sounds amazing, having all these discrete parts that do only one thing, and one thing well sounds appealing. I used to think serverless would solve most of our problems and that we will be able to write the frontend and author some functions to glue the different services together.
I don't think that anymore
Sadly, I don't think that's the case. At least not for the apps I build, and I would argue that's also the case for many of you.
Stitching services together sounds excellent in theory, but it has its own complexity. I'll try breaking down a list.
Lack of conventions
When developing a monolith, either RoR, Laravel, Django, or any other tool, you have a common way of modeling the problems, and this philosophy sticks during the development process. I think I would know how to use Rails ActionMailbox even if I never used it in the past, the framework is coherent and I know what to expect.
This doesn't happen when you use Service A for one thing and Service B for another. There's some extra work your brain needs to do when moving between specialized services.
Event-driven programming is hard
At the very core, whenever you need to use two services together and complement some missing functionality with a lambda function, you are doing event driven programming. This is, an action performed in service A may trigger a function to execute and impact your datastore, or to send an email, etc.
This model is hard to follow, things end up in a database, or in a queue without apparent connection. Similar to what happens on a monolith with model callbacks where you start getting effects that do not follow an evident cause.
Conclusion: How to decide
As usual, it depends. When the endeavor is huge, and you are building an app that needs to scale rapidly and painlessly, I think you should definitely consider serverless. Here service orchestration costs are less than what the services itself will offer in terms of scalability, reliability, etc.
The same is true when tradeoffs are minimal. Imagine a small website like Ingenious's website or a marketing site. Those have not that many moving pieces, so I think it's ok to liberate myself from thinking about the infrastructure.
But, here's the catch. Most of the apps we develop, and when I say we, I mean, most of the people I know that work on the industry -sorry, I don't have any friends at Google 🤷- are in an uncomfortable middle ground.
Their app is probably not that big that needs to scale to infinity. Neither is so small that you can hold the architecture in your head all the time.
Most of the apps I've worked on are fine running on Heroku dynos or having a few powerful DO boxes. Yeah, that may be a bit expensive, and I'm sure you can cut the bill by 50% if using serverless, but is it really necessary? What things are you trading off by doing that?
Too many times, I end up answering that it isn't worth changing. I may be getting old 👴🧉, but I prefer to be in control and to keep the same conventions thru my app even if I need to pay a bit more.
With that being said, I also think this will change rapidly. Both, technologies, and serverless services will end up providing full-stack frameworks that will mix the best of both worlds. I think Blitz.js heads in that direction, and it will be interesting to see how it evolves.
For now, I'm sticking with monoliths and plain-old-servers for the apps I need to maintain, but hopefully not for too long.