From Machines to Functions Pt. 1: A Brief History of Serverless Computing
How packaging code is becoming invisible
This the first in a series of posts about the containerization of software.
What is a function? What’s actually required to run a function on a machine? Why is it so hard, and why is it getting easier?
Let’s go back a bit.
(For those missing the context behind this image)
The Bare Metal Days: One Server, One Application
Software deployment used to mean dedicating an entire physical machine to a single application (I’m not going back to time-sharing just yet). This approach, while straightforward, led to significant resource waste as servers often ran at just 10-15% utilization. Each application required its own operating system, hardware maintenance, and physical space, making scaling both expensive and time-consuming. This is an over-simplification, but serves the purpose of talking about a “unit” of an application.
Virtual Machines: The First Layer of Abstraction
Virtual machines emerged as the first popular, major abstraction, enabling multiple isolated environments to run on a single physical server. Through hypervisor technology, organizations could run different operating systems and applications on the same hardware, dramatically improving resource utilization. However, each VM still carried the overhead of a full operating system, consuming significant memory and storage resources. This era gave birth to the cloud as we know it—Amazon's EC2 service, launched in 2006, was essentially a VM rental service.
Containers: Light, Fast, and Portable
Containers represented the next leap forward, introducing a lighter-weight abstraction that shares the host operating system's kernel. They weren’t entirely a new idea (Unix had similar concepts like chroot for years, and cgroups had been out for a while too), but because Docker made them usable. Containers showed that what we really wanted wasn't to divide machines, but to package programs. Unlike VMs, containers package just the application and its dependencies, making them faster to start and more resource-efficient.
This was a profound shift in thinking. Instead of splitting machines down, we were building up from individual programs. The real innovation wasn't just technical—it was conceptual. Containers gave us a standardized way to package and deploy applications, finally delivering on the promise of "write once, run anywhere."
Functions: The Best? Granularity
The most recent evolution brought us to function-based deployment, exemplified by AWS Lambda and similar serverless platforms. This model breaks applications down to their smallest practical units—individual functions that run only when needed. Instead of maintaining long-running servers, organizations deploy discrete pieces of business logic that spin up in milliseconds in response to events. This approach offers the finest granularity of resource allocation and billing, charging only for actual computation time used.
But here's where it gets interesting: while platforms like AWS Lambda handle the infrastructure complexity for you, they still require careful consideration of how you package and deploy your code. You're responsible for managing dependencies, either through zip files, layers, or container images.
The Development Experience Gap
This is where newer platforms like Modal come in. The realization was that the gap isn't just between programs and machines - it's between development and deployment.
Here's what I mean. When you're writing code locally, you just write code:
import pandas as pd
def process_data(input):
return pd.DataFrame(input).mean()
You don't necessarily think about containers or dependencies or deployment. You just write code and run it. But then when it's time to deploy, suddenly you have to think about all these other things. Modal's insight was: what if you didn't?
What's clever about Modal's approach isn't that it eliminates containers or dependencies - they're still there, under the hood. (I highly recommend watching Erik Bernhardsson talk about how).
What's clever is that it eliminates them from your mental model. You write code the “same” way locally as you do for production.
Consider the same example:
from modal import Stub, Image
stub = Stub("example-app")
@stub.function()
def process_data(input):
import pandas as pd
return pd.DataFrame(input).mean()
It automatically handles dependency management through static and runtime analysis. It detects your imports, builds appropriate container images, and ensures everything works consistently between local development and cloud deployment.
Ok, thanks.
The gap between development and deployment is shrinking, but it's not gone. We still have to think about scaling, about state, about data locality. These are hard problems, and they're not going away.
Early applications were monolithic by necessity, tightly coupled to their hardware. As deployment units became smaller and more abstract, developers gained the freedom to build more modular, loosely coupled systems. Modern microservices architectures, enabled by containers and functions, allow teams to develop, deploy, and scale components independently. But just ask DHH if you think microservices are the silver bullet (hint: they’re not).
The interesting question isn't just about finding new ways to deploy code—it's about further reducing the cognitive overhead of cloud computing.
Will new abstraction layers emerge that we haven't even imagined yet? Probably - it’s interesting to see what else is happening in this space. And this is mostly just on the compute/OS/containers side.
What I’m really curious about are the database oriented approaches like serverless databases (e.g. AlloyDB) or Stonebraker’s DBOS. We got “separation of compute and storage” last decade, and that was a step-function (arguably many step-functions) of improvement in query processing and data management. But it also makes the development-deployment gap for analytics all the trickier.
This post was pretty high level start to a series of deeper dives around where containerization approaches, especially w.r.t to data management, are headed (and about where they came from).
Stay tuned!