After the tour-de-force of Serverlessconf in October, I decided my entire company would be going serverless. I spent the first couple of months beating my head against the wall trying to migrate a Python Flask app to Lambda — these efforts helped me find a better way.

Six months later, we are now deploying our fourth major project serverlessly. This is how we did it — including the lessons learned (and strong opinions) formed along the way.

Lesson #1 — Ditch Python

Flask is a nice little framework for the old-time request-response style of a website with a session managed by the server. That’s quaint — but in the new world of the interactive web, it’s like trying to build a house with a rubber band and a squeegee.

The old way: Python Flask app runs on Elastic Beanstalk, storing data in RDS

As you start to move more work to the client-side to support interactions, you’ll have no choice but to use JavaScript. This usually leads to inlining into your Python templates while the Demons of Technical Debt open another line of credit for you.

Increasingly, Flask solutions become a kluge of different languages. I concluded fairly quickly that this approach was a horrific mess — and I wasn’t sure why I was using Python anymore.

After switching over to Node, everything was much more maintainable and logical, and there was no need to use more than one language. With a simple Node/Express configuration on Webpack, you can also use ES6 to eliminate the terrible JavaScript constructs poked fun at by Python developers.

Trying the same thing in Zappa/Flask was worse than doing my taxes. But in about 5 minutes, you can build a fully-fledged Node/Express app that works on Lambda like it’s the 1040EZ — it’s no big deal. So we ditched Python and joined the cool kids in the JavaScript camp.

Lambda Function As Monolith

What did we give up? Pythonistas will wax lyrically about all the cool language features, but these are mere toys compared with the practical async charms of JavaScript. And now we no longer worry about Python version 2 or 3 (is it ever upgrading?). At least for our projects, it was a very easy switch.

Of course, Ben Kehoe offers a compelling alternative sensationalist trap with his perspective and insights about using Python versus Node for serverless!

Lesson #2 — Burn the middle layer to the ground

It took us a surprising amount of time to realize an obvious benefit of serverless — possibly because we’ve been building web apps forever, or maybe it’s just that I’m getting old.

Some of our first web apps still had a Node Express layer that remembered session state either (1) by accident hoping the user hit the same Lambda container over and over, or (2) by tragedy of design where we abused DynamoDB to make it remember session IDs. What the hell were we doing?

In phase one of “The Transition”, our middle layer acted like a web server on Lambda, which is both wrong and terrible. Then we ended up with html pages filled with JavaScript calling REST APIs. This approach was thrillingly raw, desperately unmaintainable, and quickly became brittle — but we’d killed the middle layer. In serverless, the middle layer has to go.

State moves to the client, logic moves to Lambda

Lesson #3 — Enjoy the Vue

It’s great being able to jam everything into the front-end, but it’s quickly an appalling mess. You eventually stop checking in code because you’re too embarrassed to share the Rube Goldberg machine magic you’ve been building. And ‘not checking in code’ is not a good job objective for developers.

Entering the world of Single Page Applications (SPAs) exposed me to React — the most popular approach to building user interfaces. React is great but comes with a steep learning curve, lots of Webpack/Babel set up, and the introduction of JSX. While it might be something we eventually use, it was too heavyweight for our immediate needs so explore the alternatives.

Fortunately, I soon discovered Vue.js and my serverless life turn to absolute bliss. Here’s the thing: You can learn Vue in a day!

Vue’s approach to design fits nicely with our design model — everything is a component that manages its own content, design and code. This makes it very easy to manage our multiple client-projects and dispersed teams, and also work very well for a serverless mindset.

The open-source JavaScript framework gives you powerful debugging tools, great organization, and a Webpack build out of the box that will save hours. Slap on the router and store management plugins — and you can churn out realtime sexy apps like you’re a Facebook engineer. Who knew Single Page Apps could be so easy?

From a serverless perspective, Vue compiles all your goodness into an index.html and bundle.js files, primed for uploading to S3. Typing npm run build is the new compile command.

Take a moment to consider this — in the old world, we would be deploying apps via Elastic Beanstalk and monitoring for utilization, autoscaling when needed, and managing a reasonable chunk of infrastructure.

The true magic of SPAs is when you “deploy” an application, you’re simply copying index.html, bundle.js, and a handful of file dependencies to an S3 bucket front-ended by a CloudFront distribution. This gives you rock-steady distribution and loading behavior, and also enables multi-version management and any deployment methodology you prefer — just by managing text files.

We have effectively unlimited scale and only pay for what we use — there is zero app infrastructure management.

Vue essentially allows you to build a desktop application within the browser — which means you can dramatically improve the user experience. All the state can be managed here without endless request/response, you can hide latency with standard UI tricks like transition effects, and the whole application now behaves properly.

Lesson #4 — Learn to love DynamoDB

In many respects, the hardest part of getting to serverless has been truly coming to grips with DynamoDB. You definitely make a few mistakes in the first few iterations, and its tempting to ditch the whole thing and go back to RDS where everything is known and comfortable.

SQL has been my crutch for a long time, and I’ll confess to putting way too much business logic into databases. But RDMS systems are just another monolith — failing to scale well and they don’t support the idea of organically evolving agile systems.

DynamoDB is a completely different animal. When you get it right, the NoSQL database provides blistering performance, massive scale, and practically no administrative overhead. But you really have to invest the time in exploring how it works — and the initial phases are full of gotchas aplenty.

Dynamo table fields can’t contain empty strings. The point in time backup isn’t automatic. If you get the partition and sort keys wrong, you have to start from scratch with your tables. You can go from having too few tables to way too many if you try to emulate SQL queries too closely. And all the while it just feels very alien coming from RDS.

After many tutorials, trying, failing and eventually succeeding with DynamoDB, I learned …

You need to understand the way DynamoDB works, spend some time to understand indexing strategies, and how you intend to query the data. It’s very easy to jump into it without knowing all you need to know, so many people get burned and then move back to RDMS at exactly the wrong moment. Make mistakes and push through them.

One of the least-discussed joys of DynamoDB is the way you can attach code to table events using streams — like an SQL trigger that can do anything. These are extremely powerful. A very simple pattern we use is to always push table updates to an SNS topic where the changes can be ingested by other serverless code you might not have written yet.

Don’t forget that DynamoDB can feed other storage systems (RDMS, Redshift or just flat text files) and can be used to effectively smooth out the traffic spikes or protect another database from huge volumes of data. DynamoDB has a TTL feature which allows you to expire rows — which is great for staging data you want to push somewhere else.

Lesson #5 — Serverless Framework FTW

My early experimentation with Lambda was a clunky affair of coding directly into the AWS console and getting frustrated that it took a lot of work and error messages to do some trivial things. The bridge that connects your IDE to a production environment is missing.

Well, its’ missing until you discover serverless framework which is honestly the most exciting thing I’ve found in ages.

A simple sls deploy wields enormous power, bundling up your precious code and shipping it directly to Amazon’s brain in the sky. And if you need to check logs because your code is misbehaving just type sls logs -f functionname -t and you can tail your CloudWatch logs like a pro without ever opening a browser.

This. Changes. Everything. The serverless people should be showered with accolades for doing something that every cloud provider should have offered from day one. Simply brilliant. And so much win.

Lesson #6 — Authorization is the new sheriff in town

In traditional apps, you authenticate a user once and then track this person by following around a session ID. We like it because you only need to do the hard work once, and then the ID lets you cheat for the lifetime of the user’s login for however long you want that to be.

In the old world, the session ID controls access

But this approach has problems. It only works if you have that server in the middle — and we we just burned that server to the ground. It also potentially exposes you to some nasty attacks — like Cross-Site Request Forgeries (CSRF) — and doesn’t let you pass around identity to other services very easily. So this approach basically Supports The Monolith (boooo!).

We hate the Monolith and CSRF attacks — but we do like our new friend, the JWT token. I had a moment of zen-like euphoria when I learned how this works but I need a diagram to do it justice.

Step 1, get a JWT, step 2, use it to communicate with any service you write:

The first step looks familiar: the authorization process gets a JWT token

The second step is magic: any lambda function can accept and validate the token

The basic nut is that every single request is authenticated, and the client can even talk to multiple serverless activities. It’s wickedly secure, it’s anti-monolith and CSRF doesn’t even exist in JWT-land. All that’s required from your serverless code is use a Custom Authorizer to check if the JWT in the header is valid (using boilerplate code) and we are done.

JWT makes all other types of auth look overcomplicated. We switched everything to Auth0 (and Cognito in some cases) and never looked back. Serverless auth is both beguilingly simple and insanely effective, so yeah, go team.

It’s a brave new world

While I’ve worked with AWS for a long time, I’ve never been this close to the ground floor. Even in EC2 land, there was plenty of help because I was comparatively late to the party. After leaving A Cloud Guru’s serverless conference, this felt like genuinely unexplored territory and there was significantly more discovery in the dark.

In our first few experiments, we had some misfires trying to use existing tools and techniques — and the results weren’t great. After a few months getting the right stack in place, we have officially started delivering projects in a 100% serverless way. I’m confident that our migration hiccups and early exploring were well worth the journey.

We are building slick, real-time SPA apps that use exclusively serverless infrastructure, scale effortlessly and cost 70–90% less. I’m both delighted and shocked by the payoff. I’ve never been more convinced that serverless tech is going to revolutionize application delivery in the cloud.

The results are transformational.