Inside the Wood Shop
I once heard a good friend of mine say, “In the journey to buy a piece of furniture, AWS is a wood shop.”
To the uninitiated, if you’ve ever used AWS’s cloud services, their primary focus is on the APIs — damn the user experience.
I’m being harsh, but that’s because the user experience is true to my heart. I have a high bar for engineering and design as they exist together — not as two separate experiences.
AWS tries and make up for this by saying, “AWS is for builders.” That is true. But once you build there, it’s tough to move. You’ve already made everything in their wood shop, and not much works outside of it because you’ve customized all the chairs to whatever your engineers built.
I’m here to say that that’s ok. You could say the same for any provider that you choose.
As a builder in AWS, I’ve built many simple systems along with a lot of complex ones. The struggle is real. It’s tough to sew things together within AWS cloud. First, you have to know the intricate IAM permissions policy framework that each of these services uses to grant permissions. “Oh, SQS Queues have resource policies, but my Kinesis streams don’t?” The IAM policy language is challenging; some services offer tag-based conditions, some services don’t.
From an architecture point of view, all the AWS cloud resources are broken down into the most simple parts, giving them the properties of being loosely coupled. Most of the time, this is for an excellent reason to make things decomposable. But it also makes you want to scream when you have to stitch every lambda function together with every resource it consumes.
I trust that AWS did both of these things for good reason. By building loosely coupled systems, AWS avoids bureaucracy between their engineering teams at all costs. This plays into one of their main principles, “Have a bias for action!” They let every team run at full speed and make decisions, allowing them to build new features at break neck pace and out-innovate traditional command and control organizations.
Nothing is free, because that decentralized decision also comes at a price for the downstream developer. The complexity of all those cloud components and the loosely coupled nature of their integrations make it incredibly hard to stitch everything together and make a proof of concept.
For a moment, I’d like to compare Google Firebase and AWS Amplify. The Google Firebase developer experience is far superior to the AWS Amplify experience. Google simplifies everything so that developers can focus on building out a frontend and backend system, all while providing the reactivity that the internet should be in the form of real-time pub/sub data to the client without building it all yourself. This is beautiful reactivity delivered in a few command line interface commands so you can get up and running with your app. This is true innovation and is beautiful to use as a developer.
Devil in the Details
At the time I was building my Firebase, the function store wasn’t stable enough for my production services. I had folks calling me telling me about how things aren’t working while they are doing a sales pitch. True, it could happen to any cloud platform at any time. But this was happening frequently, and on their status page I could see constant degradation and inconsistencies on when these would fire. When I couldn’t decompose a service, it felt like my hands were tied. Sometimes this was fine, but other times it felt like I was going to have to move away from Firebase in favor of something more decomposable and more defined.
Since then I’ve had great success with AWS Lambda. While stitching everything together and learning how to use AWS DynamoDB vs. Google Firebase was painful, but the reality is that I’ve never been called about anything being down on AWS. Likewise, my AWS ECS container services have been up for about 4 years, with 100% uptime. Insane. (Insane when thinking about the fact that they are on Spot instances as well)
AWS DynamoDB is now a global database. AWS Lambda compute can also be global now that you can deploy it to multiple regions. And KMS and Secrets Manager are now multi-region deploys. This makes your devs super powerful at the cost of pennies.
Enter the Cloud Development Kit (CDK)
Asking a frontend developer to spin up a new cloud environment on AWS is like asking Tom Brady to choose a different position as quarterback. He’s the GOAT. Don’t ask him to play running back. You’re not going to like the results. You want your devs to be focused on creating value where they are excellent.
But imagine what it would be like if you could make spinning up resources on a whim simple and give it to your devs in a language that they are comfortable with. That’s what the Cloud Development Kit does for developers.
CDK is an infrastructure as code software development kit spearheaded by some brilliant risk takers at AWS that thought it would be a good idea to build an abstraction on top of AWS CloudFormation. Say what you will about CloudFormation; but it’s an excellent software deployment state machine that gives you real-world security. Developers used to have to run bash scripts to update servers. Today, we run `cdk deploy` to update servers that we don’t manage.
The Good Parts
As people get more involved in deploying software, they start to use cloud services because it takes away the pain of being on call for problems. Deploying code to virtual machines is better than running a server in your data center because if that server goes down, you’re on call. If the server in AWS goes down, that engineer in AWS is on call. More time doing the things you want to do, versus doing things that don’t provide value to your customers. But previously you had to do this with CloudFormation in AWS allows you to describe your cloud services so that they move into the desired state. Some folks bypassed CloudFormation altogether because it was so difficult to write JSON files that were 2000 lines long and started using Terraform or Pulumi. The CDK came along and changed all that. Now you can write infrastructure in python, javascript, typescript, golang, or C#.
#!/bin/python
import aws_cdk.aws_lambda_python as lambda_
lambda_.PythonFunction(self, "MyFunction",
entry="/path/to/my/function",
index="my_index.py",
handler="my_exported_func",
runtime=Runtime.PYTHON_3_6
)
“Abracadabra, I want a lambda function that I can deploy with Python.”
Poof — your lambda function is ready to be invoked in the first environment.
“Now I want it in staging”
Poof — it’s in staging, the same way it was in the test environment. This is how cool infrastructure as code is, and why it’s an absolute necessity to have it. You cannot test infra changes reliably without doing it within your application codebase, and then promoting those changes into the next environment. Code, infra as code, and your configs are all in your repo, giving you much joy as things are just the way they should be in heaven (prod) as they are on earth (dev).
Using a computer science analogy, if CDK is the interpreted language, I now consider CloudFormation to be the assembly language of AWS infrastructure.
Using the CDK, you can access higher-level abstractions as they are built by the community. So what does that mean for the developer? Ok, let me try to talk about a few of the cool things I’ve seen so far.
Bastion Host. Everyone wants a bastion host for various reasons. “I need to feel the network with my bare hands, so I SSH into the host and reach out” to “Can I even get to google.com on this network?” To launch one using the CDK is trivial. Just apply this code to your CDK deploy, and boom. Bastion with the ability to use SSM to get into the host without setting up audit logs with SSH access. <auditors everywhere applaud>
#javascript
const host = new ec2.BastionHostLinux(this, 'BastionHost', {
vpc,
blockDevices: [{
deviceName: 'EBSBastionHost',
volume: ec2.BlockDeviceVolume.ebs(10, {
encrypted: true,
}),
}],
});
Let’s add access for the bastion host to get a secret.
const mySecret = new secretsmananger.Secret(this, "mySecret");
mySecret.grant_read(host)
We didn’t have to write a shitty IAM policy! Sweet. Now it feels like the experience is more cohesive, while giving you the ability to granularly scope things when needed.
Want a virtual private cloud network? Use this code, and you’ll get a VPC along with 3 nat gateways. Oh, make that one because you’re cheap like me.
#javascript
const vpc = new ec2.Vpc(this, 'TheVPC', {
cidr: "10.0.0.0/16",
natGateways: 1
})
And my personal favorite, a CodePipeline Pipeline. CodePipeline is the bane of my DevOps existence. If you’ve ever used CodePipeline, you’ll remember that it’s slow, laborious, and about 1k+ lines of JSON to get anything going. Not with the CDK; it’s about the same amount of lines of code as a Github actions pipeline with more granular access than Github can provide. Codepipeline is still slow, but hey, you’re not managing servers or multiple cloud providers, right?
Honestly, as a sidebar, they should integrate the CDK natively into AWS CodePipeline instead of me needing to use AWS CodeBuild. They would speed up AWS CodePipeline and make it a first-class experience coupled with CDK.
This is what will change AWS. It will make simple patterns accessible to everyone. It will bring the world-class security and complexity they are known to bear to the developers that want to get more done with less, like me.
It will enable innovation so that user experience designers can bridge the complicated to simple to gain the next level functionality required to level up AWS. This is such an important point, a crucial moment, an important idea that I don’t even think the folks at AWS realize the scale to what they’ve done.
They could make the chair purchase simple. They could sell the cheapest, most secure, greatest uptime chair in a beautiful one-liner that every developer would love to get their hands on.
Keep pushing, CDK team. You’re bringing to bear one of the best iterations I’ve seen in a while — and it will take time. It’s not perfect and some bad decisions will be made. But, in the end, it’s my opinion that it will change the way we code infrastructure into the future so that no one needs to read assembly (CloudFormation). And for those of us that still like to read the fine print, we’ll always have that escape hatch.