The serverless revolution is alive and well in the PHP world

A few months ago, there was an article that discussed the current state of serverless computing why it fell short on its promise. People like Jack Ellis of Fathom Analytics chimed in about it on Twitter. There have been some incredible serverless breakthroughs in the last two years. And because of those, serverless has really grown in potential as opposed to stalled.

Now, this article will focus on the impact of these serverless breakthroughs for running PHP on AWS Lambda. I can’t speak for other programming languages. That said, some things we’ll go over should apply to them as well.

Performance and language support

In November 2018, AWS Lambda announced two new features: custom runtimes and layers. These two features drastically changed what you could do with AWS Lambda. Before, you could only use the runtimes that Lambda had. If they didn’t support your programming language, you were out of luck and to do some terrible workarounds. (Most of them revolved around using a node.js wrapper.)

Now, this is the first problem with serverless highlighted in the article. Serverless supports limited programming languages. Meanwhile, supporting other programming languages comes at a significant performance cost.

And it’s true! If you try to run PHP through a node.js wrapper, it’s laughably bad. No one in their right mind would use that for anything serious.

The issue is that the argument doesn’t reflect how things are today. Running PHP through node.js was something you did before the release of custom runtimes and layers. It’s not something someone would do now.

Instead, you compile PHP in a Docker container, package it up and ship it as a layer. Lambda runs this layer as a custom runtime. So now, when Lambda processes PHP code, it does it without the node.js wrapper and that huge performance hit.

And you can do this with any programming language! There’s no question that it’s more complicated than using an AWS runtime. But that’s different from saying that you can’t do it or that you get a performance hit from doing it.

Cold starts are a solved problem

Another aspect of performance discussed in the article is the cold start. If you’re not familiar with a cold start, it’s what happens when there aren’t any Lambda functions available to execute your code. When that happens, Lambda needs to create a new one which takes a bit of time. (Usually a few milliseconds.)

Cold starts have been an issue since AWS Lambda came to market. There’s always going to be a cost involved with setting up a new Lambda function. But it’s also a solved problem.

First, AWS won’t shutdown your function the moment it finishes running. So you can keep it warm by pinging it every few minutes. There are countless articles that document how to do this. Both Vapor and Ymir support keeping a number of functions warm so you don’t deal with cold starts.

But let’s say that you have a predictable amount of traffic. For example, you could guarantee that 10 Lambda functions would have near 100% usage at all times. Well, AWS came out with provisioned concurrency just for that specific scenario.

Lambda functions that use provisioned concurrency are never shut down. So there’s no warm up delays with them. You also pay less for them than if you had a function running all the time. It’s a similar idea as EC2 reserved instances.

So that’s why most people don’t consider cold starts much of a problem nowadays. If you don’t have predictable traffic, you can keep a few functions warmed up. Otherwise, you can use provisioned concurrency to keep these functions running all the time.

Vendor lock-in

So far, we’ve only talked AWS Lambda instead of just serverless. This is where the next issue brought up in the article lies. With serverless, you’re often locked in to a specific cloud provider. So, if you wanted to stop using AWS Lambda and use another cloud provider, you wouldn’t be able to easily.

There are a few facets to this issue. But the fact is that some form of lock-in is inevitable. For example, if your application uses MongoDB, you’re locked in to using it. The cost of migrating away from MongoDB is significant.

Now, the question is whether using serverless to run a PHP application has a high switching cost. And the answer is, it doesn’t have to. With platforms such as Vapor and Ymir, AWS Lambda is only there to run your PHP code.

It’s true that services like Vapor and Ymir have an integration component. Vapor has vapor-core and Ymir has its WordPress plugin. But this isn’t a tight coupling.

Laravel is still Laravel, and WordPress is still WordPress. There’s nothing in their architecture that locks you in to using AWS Lambda forever. (Besides that it’s awesome!) You could easily migrate your code back to a regular server tomorrow.

So that’s why the argument doesn’t hold much weight in this context. If you had an application built with a microservices architecture, this would be another discussion. But that’s not the only way to run applications on AWS Lambda.

Serverless can run any type of application

This brings us to another issue article brought up in the article. It’s the inability or inefficiency of serverless for running entire monolithic applications. This one is easy to refute since we’ve been talking about running Laravel and WordPress throughout the article. Both are monolithic applications that run perfectly fine on AWS Lambda.

The reality is that serverless is a natural fit for PHP. If you’re not familiar with how PHP applications, they’re nothing more than scripts that get executed every HTTP request. This makes the server architecture of a PHP application quite straightforward.

PHP server architecture

You have a web server that receives a HTTP request and forwards it to PHP. PHP runs the application script, which takes that HTTP request and turns it into a HTTP response. PHP returns that response to the web server, which sends it back. That’s it.

Switching to using serverless doesn’t change this architecture. It’s just the components that change. For example, there’s still a component that receives HTTP requests and forwards them to PHP. It’s just an API gateway that does it instead of a web server.

The PHP component also changes. You don’t have PHP-FPM managing a dynamic pool of PHP processes anymore. Instead, it’s AWS Lambda that spawns PHP processes as needed.

That’s the essence of the difference between a PHP application hosted on a server versus one hosted on AWS Lambda. It’s essentially a non-issue.

If you want to learn more about how serverless PHP works, you can check out this article.

Is this just a PHP thing?

There’s been a lot of work over the last few years to make serverless PHP a thing. It started with Matthieu Napoli and the Bref project. But two years ago, Taylor Otwell came out with Vapor for deploying serverless Laravel projects. I’m doing the same with Ymir and WordPress projects.

I’m not familiar enough with other languages to speak about them. But, as an example, there’s lamby for Rails. And I’m sure other people are looking at it for different languages or frameworks.

That’s because this isn’t going to go away. Hosting an application using a serverless platform has some clear advantages. (This thread by Jack Ellis does a good job explaining them.) That’s why I expect to see a lot of other programming languages adopting it as a hosting platform in the future.