You’ve chosen your frameworks and libraries. You’ve learned how to write code which satisfies the buzzword and performance gods. Now you need to serve it to a global audience, and make things easy to preview, to test, to sign-off, and to evolve.
But infrastructure design is difficult and boring for most of us. We just want to get our work out into the wild.
If only we had tools which would let us go, “Oh yeah! It all deploys perfectly every time” and shout, “You need another release? BAM! What’s next?”
A truth that can be hard to admit is that very often, the production environment and its associated deployment processes are poorly defined until late into a project. This can be a problem.
It makes my palms sweaty just thinking about it.
If like me, you have spent time building things for clients, you’ll probably have found yourself working with a variety of technical partners and customers who bring different constraints and opportunities to your projects. Knowing and proving the environments and the deployment processes is often very difficult, but can be a factor which profoundly impacts our ability to deliver what we promised. To say nothing of our ability to sleep at night or leave our fingernails un-chewed.
Let’s look at this a little, and see if we can’t set you up for a good night’s sleep, with dry palms and tidy fingernails.
A familiar problem
You’ve been here too, right? The project development was tough, but you’re pleased with what you are running in your local development environments. Now you need to get the client to see and approve your build, and hopefully indicate with a cheery thumbs up that it can “go live”.
Chances are that we have a staging environment where the client can see the build. But be honest, is this exactly the same as the production environment? It should be, but often it’s not. Often the staging environment is nothing more than a visible server with none of the optimisations, security, load balancing, caching, and other vital bits of machinery that we’ll need (and need to test) in “prod”.
Often the production environment is still being “set up” and you’ll have to wait and see.
In development, “wait and see” is the enemy.
Instead of waiting to see, we need to make the provisioning of, and deployment to our different environments one of the very first jobs of our project. I’ve often needed to be the unpopular voice in the room who makes a big fuss when this is delayed. I’ve described it as being a “critical blocker” during project meetings and suggested that everything should halt until it is fixed.
It is that important.
Clients don’t often like hearing a wary, disruptive voice saying “whoa there Nelly!”, because the development should be able to continue while the production environment gets sorted out, right?
Sure. But if it is not seen as a blocker, it is seen as something that can just happen later. And if it happens later, all the ugly surprises and unknowns surface later too. And later is when we’ll need to be thinking about other things. Not the plumbing. Trust me, it pays to face up to the issue right away rather than press on optimistically. The client will thank you later.
Attitudes and expectations
We should, I think, exhibit these four attitudes towards production deployment:
- Make it scripted
- Make it automated
- Make it real
- Make it first
Make it scripted
Let’s face it, we are going to need to deploy more than once over the course of the project. We are not going to get things perfect on our first shot. Nor should we expect to. And if we are going to repeat something, we want to be able to do it identically and predictably every time without needing to rely on our memories.
Developers are great at scripting things which they would otherwise need to repeat. It makes us faster and it also helps us keep track of the steps we need to take.
I’m not crazy enough to try suggest the best technology to script your builds or deployments (holy wars lie down that path). A lot will depend on your languages and your tastes. Some will like Fabric, others will prefer Gulp, you might prefer Make or NPM. It doesn’t really matter as long as you can script the process of building, packaging and deploying your project.
Wait. Won’t we need to know everything about the build from the start in order to do this? Aren’t our dependencies likely to change over time?
Yes. That would be ideal. But it’s ok. Like our code, our deployment script will evolve over the life of a project. So evolve it. Start by scripting what is needed to support the first iteration of the project, and then maintain that script. It will become a valuable “source of truth”, providing a form of documentation of what your project needs for a successful deployment. Another bonus.
Make it automated
If we have a scripted deployment which we can run by executing a single command, then we are in great shape to automate that process by triggering the build and deployment via suitable events.
Again, I prefer not to offer one single suggestion of when this should occur. That will depend on your approach to the project, how your development team is organised, and how your QA team operate. You can tune this to suit.
For one project I worked on, we chose to trigger the build and deployment to our production environment every time we used Git to tag the master branch of our version control repository. There were a few moving parts, and we needed to do some upfront work to get everything working, but that upfront effort was repaid many fold as we deployed time and time again, and exposed some issues with our environment long before we got to “launch day”.
With a scripted and automated process, we can make deployments “cheap”. This is our goal. When there are minimal cognitive or time overheads associated with deploying, we’re likely to do it all the more often and become more confident that it will behave as expected.
Make it real
Alright, we have written scripts to build and deploy our projects. Anyone tagging our repo will trigger things to happen as if by magic, but where are we pushing things to? We need to target a real environment if this is to have any value.
A useful pattern is to have all activity on our develop branch trigger deployments to our staging server. Meanwhile tagging master will deploy a version to the production environment. How we organise this will depend on our git branching approach. (I’ve seen as many ways of approaching Git Flow as I have seen ways of approaching “Agile”).
It’s vital though, that we ensure that we are deploying to, and testing against, our real infrastructure. We want to see real results. That’s the best way to learn real lessons.
Make it first
Building our site to run in an environment not yet fully defined or available to test is like climbing without ropes – it’s possible, but we put ourselves at risk. And the higher we climb the greater the risk. So it is important to do this as early as we possibly can.
Don’t have a certificate for our HTTPS yet? Fine, but let’s still deploy to this evolving production environment and introduce HTTPS as soon as we can.
Before we know it we’ll be proving that this is set up correctly and we’ll not be surprised by mixed security alerts or other nasties further down the line.
Mailchimp perfectly capture the anxiety of sending emails to gazillions of people for a campaign. But we’re lucky. Launching a site doesn’t need to be like performing a mailshot. We can do things to banish that sweaty hand.
Doing preparation work upfront means that by the time we need to launch the site into the wild, we have exercised the deployment mechanics, and tested the production environment so rigorously that this task will be boring.
(It won’t be boring. Launching should always be exciting because the world will finally get to see our beautiful, painstaking work. But nor should it be terrifying. Especially as a result of not knowing for certain if our processes and environments are going to work or burst into flames on the big day.)
What tools exist?
Well this all sounds lovely. But how should we tackle this? Where are the tools for us to use? As it happens, there are many service and tools that we can use to work this way.
All of the big players like Amazon, Azure and Google offer tools which can help us here. Google for example, can host multiple deployed versions of your project in parallel and you can manage them via their App Engine console. Each build receives its own URL which you can use to access any deployed version of your site.
Having immutable deployments which stick around in perpetuity (or until you bin them) is a key feature which unlocks the ability to confidently direct your traffic to any version of your site. With that comes the capacity to test any version or feature in its real environment, and then promote a version, or rollback to a previous version whenever you want.
A liberating power to have.
In order to create all of those different versions, we’ll need somewhere to run our build and deployment scripts. Jenkins has been a popular Continuous Integration (CI) option for some time, and can be configured to perform all sorts of tasks, giving you extensive control over your deployment pipeline.
You need to host Jenkins yourself, but it provides some simple ways to do that.
The landscape for CI is getting richer and richer. With many hosted services like Circle CI providing this kind of automation up in the cloud.
One stop shop
Netlify combines both hosting and continuous integration services. It monitors your git repositories and automatically runs your build in a container on its servers when it finds changes. Each branch and pull request in your git repository will result in an immutable version of your site with its own URL.
Netlify is unlike Google Cloud, AWS or Azure in that it cannot host a dynamic server-side application for you. Instead it specialises in hosting static, or so called JAMstack sites.
Personally, I find that its simplicity makes it an approachable option, and a good place to learn and adopt some of these valuable habits.
Full disclosure: I’m a Netlify employee. But before I was, I was an avid customer, and it was through using Netlify that I first encountered some of these principles in practice.
Conclusion. It’s all about the approach
No matter what tools or services you use (and there are many which can support these practices), the most important thing is to adopt an approach which lets you prove your environments as quickly as possible.
Front-loading this effort will cast light onto the issues that you’ll need to address early and often, leaving no infrastructure surprises to spoil things for you on launch day.
Automating the process will mean that when you do find things that you need to fix or to improve later (and you will), issuing another release will be trivial. It is a lovely feeling when you have confidence that releasing v1.0.0 will be no more stressful v0.0.1. In fact it should actually be less stressful, as you’ll have been down this road many times by then. Fixing the potholes and smoothing the way as you went.
From here, it should be a smooth ride.