Not all infrastructure as code is born equal. Some styles of infrastructure as code give us great benefits — whereas other kinds can cause a lot of headache.
Writing good infrastructure as code doesn’t mean simply bundling code that creates infrastructure into a repository and walking away. Knowing the in declarative vs. imperative infrastructure as code can mean the difference between having an easy life or a total nightmare.
By the end of this article you’ll know what the difference is between declarative and imperative infrastructure as code, why it matters and when you should use each.
To kick things off, let’s start by discussing what the terms declarative and imperative mean.
A declarative instruction could be.. “Can I have a coffee on my desk at 9AM on Monday morning?” whereas the imperative version of the command could be… “Go to that machine, then get the glass jar, the fill it with water, then put it back in the machine” … you get the idea.
Okay — so one example seems longer and more wordy, but what’s the real implications of using one style of instructions over the other?
In the imperative example (the longer one) if there were to be a problem midway through — say that the glass jar was not with the machine, what do you think would happen? If you were a computer program — you’d crash. Why? Because you would be trying to perform an action that did not exist or wasn’t even possible.
The explicitness of the imperative example makes it brittle. Whereas the declarative leaves scope for wiggle room and inference.
Now I know what you’re thinking… “That’s very abstract — how does that apply in the real world”? So let me give you some real world examples using infrastructure.
Now I know sometimes it’s hard to grasp concepts in the abstract. Below we’ve got two example ways to create the same infrastructure. In this case we’re creating an S3 bucket, first with Terraform, a declarative infrastructure as code tool. And secondly with a script. After we’ve gone through these examples we’ll cover the key differences between the styles and when to use each.
First up, the Terraform example.
As you can see, we declare the target end state of our infrastructure. We don’t know whether it currently exists or not — if we have one, we want one. If we’ve got one, we don’t need to do anything.
How would this look if it were imperative, though?
Here’s an example of creating an S3 bucket using the AWS CLI.
The difference between the declarative and the imperative is that the declarative must know the current state, it must know whether the infrastructure already exists to know whether to create it or not. The imperative however has no idea if the infrastructure exists. The imperative example also cannot be easily re-ran, and doesn’t include the ability to update or delete.
Now that we’ve seen what these bits of code look like in real life we need to understand why drawing a distinction is so important, so let’s look at the key differences between these styles.
So — we now know that declarative and imperative styles of programming are different, and what those differences are. But how do these ideas relate to engineering and infrastructure as code specifically?
Dealing with “Configuration Drift” — Configuration drift is when infrastructure changes slowly over time. Imperative styles of infrastructure as code will struggle to adapt to configuration drift since they are coded typically for one type of lifecycle: update, create, delete, etc. Whereas declarative infrastructure as code will be able to adapt more easily to changes, reporting the differences and leaving it up to you to decide how to proceed.
Ease of Repeatability — We use infrastructure as code for aspects of repeatability. When we’ve ran a script in a test environment we want to ensure that the change will apply the same in a production environment. With an imperative style of programming infrastructure can reach different states in different environments and the benefits of repeatability is lessened.
Management Of Complexity — Whether we’re writing declarative or imperative infrastructure as code we need to take into account the context. If the scenario calls for a simple update script that is written imperatively the advantage of speed and simplicity may be worth the potential down sides.
Idempotency — Idempotency is the ability to run the same command and achieve the same result. Declarative infrastructure as code can be executed repeatedly and it will create the same result whereas imperative infrastructure as code may only be able to run a single time unless it specifically has been coded to include an if-already-exists-dont-perform type logic.
State Management — Imperative scripts do not care about the current state of our infrastructure. This makes them in some ways more simplistic. A declarative tool needs to manage the current state. And managing state is not always easy. It’s entirely possible that the declarative infrastructure as code’s picture of the current state actually can differ from the reality of our state.
There’s no strict right or wrong style to use — as always, it depends. It depends on whether you’re writing a large amount of code, whether you’ll need to update the infrastructure in future, etc. Sometimes a quick imperative script is all that is required, but if you’re building more sophisticated infrastructure a declarative style is definitely preferred.
And actually that now concludes this article! Hopefully that should clear up a little more what the two terms declarative and imperative mean — at least in the context of infrastructure as code. It doesn’t matter too much if the concept seems a little abstract. Now that you know the high level differences between the styles when you come to write your infrastructure as code hopefully you’ll pause for a moment and consider which style you want before diving in.
Join the community of cloud native software engineers just like you. Keep up-to-date with the latest in cloud tech and learn fundamental skills on core cloud engineering topics like: Docker. Serverless and Linux, and more.
Join us and get ahead!