Create a good serverless developer experience
What makes a good developer experience?
I asked twitter and it was all over the place. A theme emerged:
Good developer experience is when tools make your job easier, get out of the way, and let you focus on your code
What makes a good developer experience?
β Swizec Teller published ServerlessHandbook.dev (@Swizec) November 16, 2019
How do you setup a serverless project for good DX?
It comes down to 3 features:
- Infrastructure-as-code
- Fast deploys
- Tooling for common tasks
Infrastructure-as-code
A setup that lets the developer work at their pace on building something without ever having to worry about tooling, CI, and other accidental complexity.
β that hotspot over there (@muditameta) November 16, 2019
In other words, a setup optimized for iteration and low accidental complexity introduced noise.
As mentioned in the Getting Started chapter, I like to use the open source Serverless Framework with AWS. When using Netlify and Vercel you don't need Serverless because config-as-code is baked into their philosophies.
You write a configuration file, add it to version control, and that's your infrastructure. Nothing happens outside that configuration file.
This means that:
- Your deploys are repeatable. Run deploy, get the same result every time. The same functions, the same queues, the same caching servers, everything.
- Same infrastructure in test as in prod Subtle differences between test environments and production are a waste of time. Big part of why Docker got popular.
- Share infrastructure between the team. Ever had to ask a team member what environment variable they used for a thing? I have. After 2 hours of digging into the problem and realizing it's a configuration issue. π€¦ββοΈ
- Infrastructure that always fits your feature branch A common problem are new features with different infrastructure. Like adding a new queue or cloud function. Instead of setting it up every time you test, infra-as-code can do it for you.
- Spend time in the tools you like, not confusing web UI We're engineers and we like building things. Not clicking around a web UI doing repetitive tasks that take 20 minutes.
Fast deploys
An intuitive api and very short feedback cycles.
β Mikey (@CodingDive) November 16, 2019
The shorter your feedback cycle, the faster you can work.
On the frontend we have local dev servers and hot reloading. You see the result almost as fast as you write the code.
On the backend things are trickier.
You make a change ... now what? If you have unit tests, they show you part of the picture. The specific scenarios you thought to test, the methods you're exercising, the particular inputs.
All great.
But unit tests can't tell you your system works. That's where bugs come from βΒ systems complexity.
You can simulate the environment and run your tests. That works to an extent, but it's never perfect.
Your best bet is to make deploying to a staging, QA, or production environment fast enough to use for development. With serverless, that becomes possible.
You could even set it up so that pushing to GitHub deploys every branch. Netlify and Vercel call it pull request previews.
How fast deploys work
Here's how the flow works:
- Hit deploy
- Compile your code locally on your fast developer machine. Since your code is small, it compiles in seconds.
- Compile your infrastructure the serverless framework compiles your infrastructure into a config file for the target platform. With AWS that's SAM.
- Upload your bundle this is the slowest part.
- Infrastructure sets itself up using your config the platform sets itself up. Servers appear, queues go up, etc. Takes a few seconds
- You're ready to go
Slowest deploys I've seen on production-sized backends are in the 2 minute range. That's for a system with hundreds of lines of configuration.
On my side projects it's 30 seconds.
That's fantastic compared to a Heroku or Docker deploy that takes 20 minutes.
Tooling for common tasks
I like to use my project's package.json
as a collection of scripts for common tasks. yarn
or npm run
make them easy to run.
The most common is yarn deploy
# package.json"scripts": {"build": "tsc build","deploy": "npm run build && sls deploy"}
With those 2 lines you can deploy from any branch without worry that you'll forget to build your project first. The build
script runs a typescript build and sls deploy
runs a serverless deploy.
This part gets trickier when you use multiple environments. We'll talk about that in the chapter on prod, QA, and staging environments.
Other helpful tools I've set up for bigger projects include:
yarn psql
to connect to my remote databasereset-env
to reset a remote database for testingtest-X
to run different tests against the server environmentadd-engineer
to add a new engineer-specific environment so everyone can test on their own
Any time you find yourself running the same sequence of commands, you should consider adding them as a script to package.json
. Give others that same super power :)
How this works in practice
In the next few minutes you're going to build your first serverless backend. A service that says Hello π
We're using open source technologies and deploying on AWS Lambda. You can learn about other providers in the Serverless Flavors chapter.
You'll need a computer configured for JavaScript development: Have nodejs installed, a code editor, and a terminal.
Setup for serverless work
When working with serverless I like to use the open source Serverless framework. We'll talk more about why in the Good serverless dev experience chapter.
With the serverless framework we're going to configure servers using YAML files. You write config, framework figures out the rest.
Install it globally:
npm install -g serverless
You'll need AWS credentials too.
I recommend following Serverless's guide on AWS setup. It walks you through the necessary steps on your Amazon account and a couple terminal commands to run.
Create a tiny project
There are no special initializers for serverless projects. You start with a directory and add a configuration file.
mkdir hello-worldcd hello-worldtouch serverless.ymltouch handler.js
You now have a project with 2 files:
serverless.yml
for configurationhandler.js
for server code
In future chapters you'll write backends using TypeScript. But one thing at a time :)
Configure your first server
Configuration for your server goes in serverless.yml
. We're telling the Serverless framework that we want to use AWS, run nodejs, and that this is a dev project.
Then we'll tell it where to find the code.
# serverless.ymlservice: hello-worldprovider:name: awsruntime: nodejs12.xstage: dev
Our service is called hello-world
and there's a couple details about our provider. The stage
tells the difference between development, QA, and production deployments. More on that in the Dev, QA, and prod chapter.
Let's tell our server how to run code.
# serverless.ymlservice: hello-worldprovider:name: awsruntime: nodejs12.xstage: devfunctions:hello:handler: ./handler.helloevents:- http:path: hellomethod: GETcors: true
We started a functions
section.
Each entry becomes its own tiny server βΒ a serverless lambda. Together, they're the hello-world
service.
The hello
lambda calls an exported hello
function inside our handler.js
file when a GET request hits /hello
.
All that from these few lines of code π
PS: enabling CORS lets you call this function from other websites. Like your frontend app.
Write your first backend function
Backend functions in a serverless environment look like the JavaScript functions you're used to. Grab arguments, return a response.
Add a hello function to handler.js
// handler.jsexports.hello = async (event) => {return {statusCode: 200,body: "Hello π",}}
It's an async function that accepts a trigger event and returns a response. A success status with a Hello π
body.
That's it. You wrote backend code. π€
Deploy your first serverless backend
To deploy, we run serverless deploy
.
And your server is up.
You get a URL for your lambda and some debugging output. My URL is https://z7pc0lqnw9.execute-api.us-east-1.amazonaws.com/dev/hello
, if you open it in your browser, it says Hello π
Try your function
Leave payload empty for GET requests, valid JSON for POST.
I'll keep it up because it's free unless somebody clicks. And when they do, current AWS pricing gives me 1,000,000 clicks per month for free π
What you got
The Serverless framework talked to AWS and configured many things.
- API Gateway to proxy requests from the internet to your function
- Lambda to run your code. This is a tiny container that wakes up when called.
- CloudWatch logs to collect logs from your code. Helps with debugging.
All those are configured for you. No UI to click through, no config to forget about next time, nothing your friends have to set up to deploy the same code.
Exciting!
Next chapter, we talk about designing your serverless architecture.