DEVOPS-05-002-P · Primer — Declarative vs Imperative Infrastructure
The idea in one sentence
Imperative infrastructure means you write the steps to reach a state; declarative infrastructure means you write the state you want and let the tool figure out the steps.
Why it exists
Imagine you want three web servers running. The imperative way is to write a script: "create server 1, create server 2, create server 3." It works — the first time. But run that same script again and now you have six servers, because the script never asked "how many already exist?" It just did what it was told, step by step.
The imperative model captures how: an ordered sequence of actions. That's exactly how humans first automated things, because it mirrors what we'd type by hand. The trouble is that infrastructure changes over time — someone deletes a server, a machine crashes, you edit the target from three to five. An imperative script has no idea what "already true" looks like, so you become responsible for writing all the "check if it exists first" logic. That logic is where bugs and drift hide.
The declarative model exists to move that burden off you. Instead of steps, you describe the desired state — "I want exactly three servers, on this network, this size." The tool inspects what exists right now, computes the difference, and does only what's needed to close the gap. Run it once or a hundred times: you always end at three servers. That property — same input, same end result, no matter the starting point — is why declarative tools like Terraform and Kubernetes dominate modern infrastructure.
How it works
Every declarative tool runs the same three-beat cycle:
Read desired state. You hand the tool a description of what you want. In Terraform this is your
.tfconfiguration; in Kubernetes it's thespecfield of an object ("I want 3 replicas").Observe actual state. The tool asks the real world what exists right now. Terraform records this in a state file and refreshes it against the cloud API; Kubernetes reports it in the
statusfield of each object, kept up to date by the system.Reason about the gap and act. The tool diffs desired against actual and produces only the actions that close the difference. Terraform shows you this diff as a plan before
applyruns it. Kubernetes runs it continuously: a controller loops forever, comparingspectostatusand nudging reality towardspec. This drive toward matching desired state is called convergence — like a thermostat that keeps acting until the room hits the temperature you set.
Contrast that with the imperative model (a Bash script, an Ansible play written as raw commands): it executes your ordered steps directly, with no built-in diff. It doesn't ask "what's already here?" unless you code that check yourself. The declarative tool bakes the diffing in — that's the whole point.
The one gotcha
Declarative does not mean "no order." Under the hood the tool still runs steps — it just decides the order and count for you, usually from a dependency graph (a network must exist before the server that lives on it). Beginners assume the declarative file describes a sequence they can read top-to-bottom. It doesn't. The order of lines in a Terraform file or Kubernetes manifest is mostly irrelevant; the tool derives real ordering from dependencies. If you find yourself thinking "but I need step B to run after step A," express that as a dependency (B references A), not as line order — otherwise the tool is free to run them in any order it likes.
See it — desired state closing the gap
flowchart TD
A["Desired state<br/>(I want 3 servers)"] --> C{"Compare<br/>desired vs actual"}
B["Actual state<br/>(2 servers exist)"] --> C
C -->|"gap = 1 missing"| D["Plan only the<br/>needed action"]
D --> E["Create 1 server"]
E --> F["Actual now = 3<br/>Converged"]
F -->|"run again"| C
C -->|"gap = 0"| G["Do nothing<br/>(already correct)"]
The one-line takeaway
Write the end state you want and let the tool reason about the gap — don't script the steps yourself, or you own every "does it already exist?" check.
Where you'll meet it
You'll meet the declarative model the moment you touch Terraform (plan shows the gap, apply closes it) or Kubernetes (spec is desired, status is actual, controllers converge the two). You'll meet the imperative model in shell scripts, in Ansible plays written as bare command lists, and in any "runbook" that reads as numbered steps. Knowing which model you're in tells you who owns the "check what already exists" logic: in imperative tooling that's you, and in declarative tooling that's the tool.
Named concepts: declarative model, imperative model, desired state vs step sequence, convergence
