Lambda is my favorite recent innovation
The serverless concept is the most important and exciting innovation in years.
I've been kicking this article around in my head for the better part of a year now, trying to come up with a good explanation for why I think web application development has changed forever. Since the advent of inexpensive smartphones, pretty decent cell coverage, person-to-person messaging and social media, constant connectivity has really changed how we interact with the internet.
For example, the concept of "Cyber Monday" in ecommerce was born because everyone would get back to their office desks on the Monday after Black Friday, and would do all of their online shopping from their workplace. That still somewhat happens, but for the most part people are doing shopping from their phones on Black Friday itself. This is how constant connectivity has changed our lives and how web or native apps can go from nothing to millions of users overnight.
So for this article, I'd like to talk about Lambda and the serverless movement -- I'll use those terms interchangeably but you can swap in Google Cloud Functions and Azure Functions here -- but not just from a technical view. Lambda is important in how it opens up application development to everyone, and that's what makes it so much more exciting than any other innovation I've seen lately. Let's start with the geopolitical ramifications.
The democratizing power of Lambda and scale-to-zero.
I've touched a little bit about the democratizing power of the cloud in previous articles, but let's go into more depth. First of all, what that term means to me is about opening up access to more developers; underprivileged developers who have a million-dollar-idea now have an inexpensive platform to develop and grow that idea.
Historically, the barriers to entry of web application development have been insanely high. EC2 made those barriers drop by a huge amount -- I don't have to build out my own rack in a datacenter anymore -- but there was still that requirement of systems management knowledge and understanding the pain points in an architecture as it scales up.
Lambda makes nearly all of that knowledge obsolete. An underprivileged developer can study a little bit of Javascript and create an API that millions of users can use, instantly. And what's even more important is that the price of rolling out a Lambda function is near zero. That same underprivileged developer can scale their idea for almost no cost, and then as their idea gets traction, they'll see the same returns that a high-powered Silicon Valley team would see.
That's what the internet is meant to be.
Lambda makes it easy to bootstrap a business from nothing.
Quick: what is your EC2 instance doing when it isn't serving requests? Or, what is your 2U racked server doing when nobody is using your application? The answer is that they're lighting dollar bills on fire and releasing the resulting carbon into the atmosphere.
What is your Lambda doing when it isn't serving requests? Nothing. What does it cost? Nothing. This is the concept behind "scale-to-zero". Cloud providers are able to successfully provision their hardware to be much more efficient than you ever could, so let them. The idea that hosting your application costs nothing when it isn't in use is a bootstrapped business owner's dream.
I can write Lambda functions, deploy them for nothing, and see about getting initial traffic and traction. If I do get market fit, if I do get traction, then my Lambda functions will scale with traffic and revenue at a predictable rate. My costs are my team's time and I don't have to worry about spending a ton of time up front provisioning infrastructure because Lambda takes care of all of that for me.
Your development velocity will increase.
Imagine an environment in which you have a consistent API to how data enters into your system and a consistent requirement for the data that your application needs to return. All your team needs to worry about is your business logic - transform the incoming data, apply that to your business model, and return the necessary response. Lambda is this magical, mythical unicorn rainbow environment.
Most of your time will then be spent in writing unit tests for your business logic, and then writing the actual business logic itself. There's no wasted time here, no integrating http middleware, no worrying about crazy routing, none of that traditional spinning your wheels on ancillary protocol-level code. Tons of teams spend their first few sprints of a project just figuring out things like API contracts, hosting, and premature optimization for scaling their application.
Lambda is a competitive advantage here -- you can and will move faster with Lambda. That efficiency gain is almost like having additional developers on your team, because debugging, deployments, testing, and all of those hard monolith-y pieces become much, much easier in a Lambda environment.
A cost comparison from 2008.
So I want to give a quick comparison of the kinds of costs savings you'll see here. In 2008, we rolled out a new version of the Skechers ecommerce site, including a new architecture - this was an almost complete rewrite from the ground up. I overprovisioned hardware because we were going to get into Superbowl commercials, and I expected 50x traffic pretty quickly.
We had a traditional 3-tier architecture. Web servers to serve static content, application servers to generate pages, and a cluster of database servers. In total, we had about 20 of these Dell Poweredge servers, 1U and 2U machines depending on the power we needed. The total hardware cost was around $75k with some fast drives, plenty of RAM, etc.
That cluster was able to serve a million pageviews per day without breaking a sweat (mainly because of the crazy amount of caching that we were doing and my incredible team). That's a $75k capex that we would replace every four years, plus opex for network operations salaries, electricity, maintenance, etc. I won't even include the cost of the load balancers, routers, backup systems, etc., just because that would make this cost comparison so completely unfair.
Let's say that every pageview gets us 2 API requests - so 2 million API requests per day. For Lambda and API Gateway, given a reasonable amount of RAM and execution time, that translates into a monthly bill of $210 for the API Gateway calls, and around $65 monthly for the Lambda costs. (As a side note, if you ever wondered where Amazon's high-margin services are, it's in things like API Gateway. I wouldn't be surprised if API Gateway was Amazon's highest-margin service.)
In other words, if you counted capex only, the opex of Lambda over that same four-year period is less than a fifth of the cost of those physical boxes. Consider that you'd need a full-time network ops engineer to maintain those machines, add in the opex of maintaining boxes, and the cost savings of running Lambda just gets ludicrous.
And the best part? Lambda means that I don't have to pay for all 20 of those Dell boxes running their fans ejecting heat into the atmosphere when it's 3am and only a few people are hitting the site.
This is why, for every project I start now, my very first statement is "we aren't buying any boxes at all." Why would you? Unless you're doing millions of transactions per second, Lambda is your best solution on day one of your project.
Lambda can be your backbone.
The really fantastic thing about Lambda is that it isn't limited to the the HTTP request and response cycle. When most people think about Lambda they think about replacing an Express server, or a Java app server, or what have you, but I have deployed Lambda code in response to Dynamo data changing, SQS queue messages, and other events, and that consistency of Lambda can be applied across your entire architecture.
Want to deal with a large amount of backpressure from an event producer? Send those messages into an SQS queue and attach a Lambda to consume that queue. The Lambdas will spin up or down based upon the volume of messages flowing into the queue, and you won't have to overprovision CPU or virtual machines to deal with spikes of traffic. Again, this is massively freeing for development teams.
Do you need to take action when data gets created or updated in your database? Use Dynamo and attach a Lambda to those data events. Instead of chaining together a ton of business logic inside a method to create a user, you could have multiple lambdas run against the user record when it's created in Dynamo, and that kind of "it's working, let's leave it be and move on" strategy will let you roll out new features quicker instead of trying to shoehorn those features into existing code.
Do you want to automatically make changes to a file when it gets uploaded? Attach a Lambda function against your S3 bucket and whenever someone uploads a file, you can run a Lambda against the file itself. I can't tell you how many "create a thumbnail for this image" lines of code you wind up saving inside your HTTP routes once you have a Lambda doing the heavy lifting behind the scenes.
Want to take an action when a mailbox receives an email message? Point Amazon's Simple Email Service receiver against a Lambda function and whenever someone emails that inbox, you'll be able to run code against the message. This functionality alone makes a sea of procmail scripts and shell/perl/etc programs completely obsolete. Here's what's crazy: if you took the price of SES out of the equation, running Heluna completely off Lambda would cost around $50 per month in CPU costs. (Again, SES is one of those high-margin services for Amazon. Running Heluna off SES would cost $2000 per month at its current volume.)
There are plenty of other examples here, but you can see what I'm getting at. Lambda can power your entire project, and can do so in such a way that it scales up and down with usage, all while being amazingly cost effective.
The technical challenges to Lambda.
So let's tackle the elephant in the room, one which Lambda detractors bring up as their number one argument against moving to Lambda. Cold-start times inside a VPC.
First, let's define a cold start: when your Lambda hasn't been accessed in a while, Amazon will turn off the container hosting it, and the next time you need to invoke that function, the container will be spun up again before your function gets executed. That takes time.
Now, let it be said, cold-start times for Lambdas outside of a VPC have improved drastically, and Amazon has gotten better not only at spinning up functions faster, but also keeping those functions warm for longer. There is only a very small penalty for cold starts outside a VPC.
Inside a VPC it's another matter - if you need to write to a local RDS database, or access a local ElastiCache instance, both of which live inside your VPC, you're going to see a massive spike in latency: as of this writing, it's in the neighborhood of 7-8 seconds. This is essentially a killer for web response times, but there's light on the horizon.
First - Amazon is promising to speed this up. Almost the entire time is creating the elastic network interface and attaching it to the container, and if Amazon is able to cut this time, then the entire cold start argument will become obsolete.
Next - if you think about how your users access data, you can architect in such a fashion that you don't need to have your Lambda functions run inside a VPC. Want to put data into a database? Use Dynamo! Have an absolute need for that data to get into a SQL database? Okay-- create another Lambda that takes the data from Dynamo and puts it into RDS. That asynchronous lambda can take all the time in the world to spin up.
Do you really need to use a cache, or can you just grab data directly from Dynamo? If you absolutely need a cache, Dynamo has its own in-memory cache for nearly the same price as Elasticache. Just use that and write all of your code to just hit Dynamo.
To sum up - once you get into that Lambda way of thinking, the one big technical challenge tends to go away, and Amazon is busy working on completely eliminating it. That sounds like a technology ready to use today.
Let's make a sample Lambda.
Quick, let's write up a Lambda to add two numbers together when they're posted: numberA and numberB.
Here's a serverless.com yaml definition for our function:
service:
name: add-api
provider:
name: aws
runtime: nodejs8.10
stage: ${opt:stage, 'dev'}
region: 'us-west-2'
memorySize: 256
timeout: 10
logRetentionInDays: 7
package:
include:
- src/**
exclude:
- node_modules/aws-sdk/*
functions:
add:
handler: src/add.handler
events:
- http:
path: add
method: post
cors: true
...and here's our code, in src/add.js:
export async function handler(event) {
return Promise.resolve().then(() => {
if (!!event.body && event.body.length > 0) {
const json = JSON.parse(event.body);
if (!!json && !!json.numberA && !!json.numberB && !isNaN(json.numberA) && !isNaN(json.numberB)) {
return json.numberA + json.numberB;
} else {
return Promise.reject("Please post numberA and numberB to add.");
}
} else {
return Promise.reject("Please post two numbers to add.");
}
}).then((results) => {
return {
statusCode: 200,
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Credentials': true,
},
body: JSON.stringify({sum: results})
}
}, (reason) => {
return {
statusCode: 500,
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Credentials': true,
},
body: JSON.stringify({error: reason})
}
})
}
This is obviously a very simplistic function, but it deals with POSTing data, simple data validation, and business logic. We define the service inside a serverless yaml template, and we can then "serverless deploy" our function so that we can have an infinitely-scalable service running within minutes.
I wanted to be a little bit more verbose around the data parsing and the output, but you can see a very predictable API for incoming data and outgoing responses. Once an engineer can wrap her head around this, spinning up new REST endpoints becomes extremely fast.
Where is serverless going to lead us?
There are two main places where I feel we can build upon the successes of Lambda / Google Cloud Functions / Azure Functions. First, the overall ease of use and learning curve. The serverless framework does a great job at hiding a lot of the low-level grit, but there's still a good chunk of yaml knowledge that you need going into a project. There's an opportunity to make an "easy mode Lambda" where I just define my functions, and an extremely-opinionated framework deploys them for me.
Second, as the price of running Lambda functions comes down and the cold start time evaporates, we will hit an inflection point where Lambda becomes the default for rolling out new APIs. I'm not entirely certain we aren't already there, but everyone using SQL via RDS will claim otherwise. (Maybe it's time for you to switch to Aurora serverless?)
These two pieces -- usability and cost -- will wind up hitting a sweet spot where new projects just get started on Lambda, and new developers just work against Lambda instead of graduating from the Express / Tomcat / Heroku / what have you school of application server development and deployment. We'll start seeing less and less devops-y kind of roles, with developers working directly against Lambda and just running their testing locally prior to doing production deployments.
Teams that are historically waterfall development shops are going to be left by the wayside; being serverless makes it extremely hard to be an organization that does 2 or 3 deployments per year, and for good reason. Rolling out new features, new functionality, and hardening your code is a continuous process, a continuous delivery, and teams that don't embrace this paradigm will see other organizations moving much faster.
What I've learned from deploying Lambda.
To wrap up really quickly, I feel like the past couple of years of deploying Lambda code have really changed how I think about web development and how I think about putting teams together. If you aren't doing any operating-system-level work, if you aren't interacting directly with filesystems or unix applications, if what you're doing is a REST API that hooks into a database, then Lambda should be the first thing you investigate as being the backbone of your stack.
Like every other technology, there is a learning curve to Lambda, but that curve isn't particularly steep, and once you're in the Lambda-frame-of-mind, you'll find that there is very little you can't do. Not having to worry about refactoring your code for scale is an engineer's dream scenario, and one which you can have out of the box with Lambda.
The challenges with Lambda are almost entirely performance related, and only in very certain situations. Amazon is working to get rid of these challenges very quickly, so even taking them into consideration when architecting a new system is almost not worth your time.
The pricing model of Lambda may be a bit confusing, but once you've deployed a few functions and you can see how your memory and CPU usage pan out, you can tweak the configuration of your functions for that optimal balance between cost and performance.
Finally, I can't wait to see what a new wave of developers from nontraditional backgrounds, locations, and environments come up with given their Lambda usage. I think we're going to see a ton of massively successful businesses built by very small teams, something that wouldn't have been possible only a few years ago.
The cover image is from the JWorks tech blog.