Serverless isn't just trendy—it's rewriting how software scales.
Netflix streams billions of hours without servers. Coca-Cola automates workflows without infrastructure. Figma and T-Mobile ditch downtime. What do they know that you don't?
The secret? Event-driven serverless code. It's the backbone of apps that scale instantly, cut costs, and survive traffic spikes. No servers. No guesswork. Just code that reacts.
This isn't hype—it's a blueprint. Ready to build smarter? Let's break down how event-driven serverless turns scalability from a challenge into a reflex.
Brief Intro to Serverless
Spoiler alert: servers are still there.
There are multiple definitions for this term online, often filled with complex jargon. The best way I like to define it is:
A "fashion" of deploying your code where YOU don't have to think about the servers running your code.
Let's take an example:
Take Bob. He built mytrashcode.com but panicked at "server setup." He's a developer, not a sysadmin. Instead, he uploaded his code to a cloud provider. They handled security, scaling, and traffic—his site went live. No late-night server meltdowns. No panic during traffic surges. Done.
Why Can't I Manage My Own Servers?
Managing your own servers usually takes one of two paths. You either run physical hardware—like turning an old laptop into a DIY server—or rent a Virtual Private Server (VPS) from providers like DigitalOcean Droplets, Azure VMs, or AWS Lightsail. These fall under IaaS (Infrastructure as a Service), where the cloud company provides the bare-metal infrastructure, but the rest—updates, scaling, security—is entirely up to you.
Does this mean self-managing servers is impossible?
Not at all. Plenty of teams still do it. But managing your own servers comes with a lot of... challenges, including:
- Knowing how to manage infrastructure/hardware.
- Setting up auto-scaling and downscaling.
- Periodically applying system patches and updates to avoid exposing vulnerabilities.
- Configuring proxies, SSL certificate generation, network settings, etc.
Dividing Your Code into Functions
Serverless code doesn't need to be monolithic, i.e., all code doesn't need to be in the same place. It can be a collection of bite-sized, event-triggered functions.
A Function is nothing but a set of code that performs a specific task. When writing your entire code serverless, you'll find that you can divide your code into various functions, each handling a specific part of your application. Let's understand this more deeply with an example:
When Bob first logs into mytrashcode.com as a new user, the system triggers a "send welcome email" function before redirecting him. Subsequent logins bypass this function entirely, routing him straight to the dashboard. This separation serves a critical purpose—while 99% of users interact solely with the dashboard, isolating secondary functions (like email triggers) enables independent scaling.
Though trivial in this example, the cost implications compound dramatically at scale. Each decoupled function operates on its own resource allocation curve—high-frequency features like dashboard access demand consistent infrastructure, while one-time actions (welcome emails) can scale down during inactive periods. This modular approach prevents overprovisioning for rarely triggered events, even before considering complex systems with hundreds of interdependent functions.
Where to Deploy???
Okay, so just a quick recap—we now know:
- Deploying serverless is great!
- Dividing your code into functions is modular and scalable.
- Functions can be triggered by events.
So, where do you deploy this architecture? Leading platforms like AWS Lambda, Azure Functions, and Google Cloud Functions support it, but we'll focus on Appwrite Functions.
Appwrite, an open-source Backend-as-a-Service (BaaS), bundles authentication, databases, storage, and serverless functions into a single toolkit. This tight integration streamlines deployment—instead of managing fragmented cloud services, Appwrite centralizes backend logic, letting you deploy event-driven functions with minimal overhead. For developers prioritizing simplicity without sacrificing scalability, this unified approach reduces operational friction significantly.
So, let's deploy our function!
Deploying Your First Function
Before writing code, set up your backend on Appwrite:
- Go to appwrite.io and register or log in.
- Create an organization (if new).
- Create a new project.
- Copy your Project ID for later use.
Now, let's simulate a server-side project using the node-appwrite
package:
- Create a project directory:
mkdir my-project
cd my-project
- Install the Appwrite CLI and initialize your project:
npm init -y
appwrite init
- Install dependencies:
npm install dotenv node-appwrite
- Create your function using the Appwrite CLI:
appwrite init function
For the runtime, I selected Node 20, but you can choose any runtime.
- Write your main function in
src/main.js
:
import dotenv from "dotenv";
import { Account, Client, Users } from "node-appwrite";
dotenv.config();
const client = new Client();
client.setEndpoint("https://cloud.appwrite.io/v1");
client.setProject(process.env.PROJECT_ID);
const users = new Users(client);
const account = new Account(client);
const main = async () => {
await account.create(
"test-user",
"test@test.com",
"test@123",
"test",
);
const session = await account.createEmailPasswordSession(
"test@test.com",
"test@123",
);
console.log(session);
};
main();
Add a
start
script inpackage.json
to runnode src/main.js
.Create a
.env
file with the required environment variables.
This function simulates a new user creation and login, logging the session details.
Note: Replace the email IDs with actual emails to receive the email.
Now, let's set up the function logic. Navigate to functions/your-function
where your function resides.
For this demo, we'll use Resend to send emails:
- Install the
resend
package:
npm install resend
- Update
src/main.js
with this code:
import { Resend } from 'resend';
// https://appwrite.io/docs/advanced/platform/events
export default async ({ res, req, log }) => {
const resend = new Resend(process.env.RESEND_API_KEY);
await resend.emails.send({
from: 'hello@yourdomain.com',
to: req.body.email,
subject: 'Hello!',
text: 'Hi, its nice to meet you!',
});
log('Email sent successfully');
return res.json({
success: true,
message: 'Email sent successfully',
});
};
You need to set up an account on Resend to get the API Key. Resend also requires you to connect to your own domain to send emails. You can read more about it on the Resend docs.
Now, let's push the created function to the console using:
appwrite push functions
The final step is to set up the event that connects the two pieces of code together using the users.*.create
event:
- Go to the Appwrite console and navigate to your created project.
- Navigate to the Functions tab.
- You should see your newly created function there—click on it.
- Go to its settings and under the Events section.
- Add a new event to trigger this function:
users.*.create
.
And... voila! Your program is done. If everything is set up correctly, running your main script should send the newly created user an invitation email. Try it using:
npm run start
Conclusion
In conclusion, serverless architecture is more than just a passing trend—it's a transformative approach to building and scaling modern applications.
Platforms like Appwrite further simplify the process, offering a unified backend solution that integrates seamlessly with serverless functions. Whether you're a solo developer like Bob or part of a larger team, adopting serverless can turn scalability from a daunting challenge into an effortless reflex.
Thanks for reading!
You can also connect with me here: https://www.chiragaggarwal.tech/
Top comments (15)
Mmm let's pretend for the billionth time that we're all the same as Netflix, and break down a working monolith serving a small number of users into 1000 functions, which all have to be maintained and spun up independently and which communicate with each other via rest. Won't that be fun! No downsides!
I absolutely agree! You never have to compare your projects to giants like Netflix's infrastructure. But case studying these practices help us in understanding why these extremely smart people working for these companies opt for these practices.
Ofcourse there are downsides , mainly complexity which your app might never need. But at their scale, it's a necessity.
Hope that helps!
"Extremely smart" is a stretch, in reality they are just like anyone else. And even if they are actually very smart, it does not mean that they don't make mistakes. Studying their solutions is definitely interesting, but not necessarily useful or applicable anywhere else.
No application needs complexity. The ideal solution must be as simple as possible because complexity is what you pay for every single minute the application is in development or production. Huge companies can address complexity by having large teams, but this rarely a solution for middle or small enterprises (often for big ones too). Moreover, complex solutions mean that people who created it not as smart as you think they are.
Serverless or event driven doesn't mean microservices. Using serverless doesn't have to be complicated, in fact it can be way more simple and cost effective than managing all the overhead needed with server or container based solutions. I used to be sceptical of what serverless could provide in real world application but after the success I have seen with Baseline as well as other startups/scaleups using serverless, it is hard to ignore the technology. If you bundle it up into a monorepo things become much simpler. Serverless has come a long way in recent years, I would suggest looking into it a little more before grouping it in with microservices like issues. Leveraging the cloud is best done by building cloud native solutions. Something like a startup that has limited resources this stuff can be a life saver, scales on demand, cost efficient, easy to hire for, less to maintain, less to secure.
I'm sorry, cost efficient how?
Simple how?
Alright, here's an example of something I can do on a 10$ a month VPS, you tell me how you'd do it serverless and how much it would cost:
Fully dynamic content website, discoverability and SEO for all pages, websockets for live data, under 100ms load times, 100 lighthouse score, able to handle over 100k users on at the same time with no issue, cloudflare ddos protection in front and a reverse proxy that blocks any traffic that isn't forwarded from cloudflare (no bot scans), multiple login methods including oauth, email sending/receiving, postgres with zero latency (local) and redis cache (local).
All of this without cold starts and vendor lock in or using a crappy web UI to configure things.
100k users is enough for 99.9% of websites but if you wanted more, my setup already has nginx as reverse proxy and it could easily be split off to a dedicated vps to handle load balancing.
To be clear, I'm no tech wizard either. I just took a day or two to think about my architecture, and now I can change anything I want about the stack at a moment's notice and at no cost.
Unless I missed a major breakthrough in serverless, doing what I did would be worse in every possible way imaginable, but most importantly, worse for the end user experience.
Also Netflix as the measuring stick is hilarious, their app is notoriously buggy and slow.
Some of it is unavoidable by the nature of the service and how many people use it, but that doesn't excuse the remainder (in reference to the other guy's comment).
Use the right tool for the right job, team size, skill sets, problem scope etc. If your needs are met by your solution keep using it, sounds like you have it under control for your case.
I suspect your cost significantly increase once you have more developers, heavier app requirements, multiple clients, integrations, staging and production environment. Don't forget about uptime, reliability and resilience, if your VPS goes down so does your app, AWS serverless will have the redundancy for all the data centers in the region (in mine there are 3 in different cities). So to compare the $10 VPS, you would be looking at least 1 running, 1 redundant, production(so 2), staging(likely 2 but probably 1 right?), so minimum $30 for a real world app and team. Ignoring the scaling, multiple regions, further provisioning and your database replication. There is also the security, backup, monitoring, operational, software/os side of things but you get the idea, there is more to the picture than just what you have mentioned.
I have hosted apps which have a marketing site, user portal, admin portal, integrations, staging & production, 100k users for less than $2 a month, obviously if you add more hungry services it will cost more 💸 I have around 20 example apps up and running that cost less than $10 in total since they only cost me when they are running and exceed the free tier, and they could all scale on demand if needed without any changes. Spiky workloads are perfect for serverless, heavy continuous load might be the time to re-evaluate. Since we are using express anyway it isn't too hard to re-platform.
Vendor lock in, sure, but how often do you change cloud provider really? I really hope that serverless does become less vendor specific in the future so that this becomes a non-issue.
Cold starts are really not much of an problem, worst case usually 700ms, then roughly 75ms including auth, database hit and return trip to user. Actual Lambda compute time is less than 5ms for simple CRUD endpoints. If you are server side rendering, then yes that is not a good idea, but for a SPA on a CDN or mobile app, it is not an issue.
I would love a better solution for server side rendering, you can technically provision/warm lambdas but it can blow out your cost as well. Just look at open nextJS for an example of issues. There are ways around it as well, but they are more complicated than I would like, so I agree on that. Caching could be cheaper for serverless, I haven't seen a great example.
When I started using serverless running locally was an absolute nightmare, Baseline has a nice solution that works well and has everything glued together, it is lighter than running a VM or docker.
Being IaC you can still have a new stack at a moments notice and with no cost, not sure what you mean here.
I used to think server/container based solutions were simple and made a lot more sense but since using serverless for real world applications it has easily become my preferred solution and has been more cost effective. There is just so much less to worry about and maintain, e.g. network, scaling, patching/updates, monitoring. However there are things that still do not make sense for serverless, and that is fine, it isn't the answer for everything.
Either way there is always tradeoffs no matter which solution you pick.
Worth a try if it is as easy I say right?
No progress in serverless makes it simpler to test, for example.
What are you testing? It shouldn't be more difficult than for other architectures since unit tests are for the code not the architecture.
Do you do only unit testing? How about integration testing?
It depends, I do what makes sense for an application, if I am prototyping an idea I am not going to write integration test, if I need to create something that absolutely cannot go down I would implement some pretty robust tests and of course build, lint, cfn-lint and something like synk for security. Just don't go too far to test for the sake of testing. If you are using mocks to do integration testing you are still just testing code. However you should be using a pre-prod environment to deploy to first regardless (built into Baseline already). You could run it locally in the CI/CD pipeline and run tests. Alternatively deploy to the cloud and run end to end tests, which would be much closer to a prod like environment. Especially good if you need to test performance. CloudFormation will automatically roll back if it fails during deploy as well, the Serverless framework will catch issues in your IaC before it deploys as well. If you need more than staging & prod environments than just spin up another one since your setup is repeatable with IaC. You could argue that running locally isn't the same as running it in the cloud, sure, I agree - especially around IAM permissions, however it is probably going to catch most of your issues, either way you can just deploy it to the cloud and test. Do you have a specific issue that you have faced with testing serverless? I would be interested to know what your experience was.
As you already described, this is getting too complicated overall. Testing of the regular apps is much simpler infrastructure-wise.
About mocks: any mock makes assumptions about the service it mocks. If assumptions don't match real behavior or real behavior changes, we're in trouble because tests are no longer relevant (and we don't know that).
Great article! Pretty interesting setup. Baseline does something similar but it feels like working on a monolith but deploys out into grouped endpoint functions and event driven lambdas. Great technology to build apps on no matter which provider you go with. Something like Baseline just gets you to a working app a bit quicker.
👍
Liked it!!! Will try it soon
Absolutely do!