Infrastructure as Code
Intermediate 11 min read Infrastructure as Code

Declarative vs Imperative

Say what, not how

Declarative states the end goal (ensure 2 app instances, port 8080 open); imperative spells the steps (create instance, open port). Cloud-native tools converge; scripts drift — that gap is the whole debate.

declarativeimperativeidempotentdesired stateconvergedrift
Prerequisites: IaC Principles
Before we start

What you will learn

  • Distinguish desired-state from step-by-step control
  • Explain idempotency and convergence
  • Know when imperative is still the right hammer
In 60 seconds

Imperative = verbs (run, create, delete) in order; declarative = nouns + properties (vpc {} with cidr) and the tool decides the verbs. Idempotent declarative tools converge any drift; scripts that run twice often do it wrong the second time.

Introduction

What is this?

Example: 'create s3 bucket' (imperative) vs 'ensure bucket exists with policy X' (declarative). When does a server get configured → chef/ansible can be declarative-at-runtime; Terraform is declarative for provisioning; Helm declarative for K8s; kubectl apply vs create/run (hybrid). Idempotency: same input ⇒ same outcome (apply twice = no second delta). Convergence: tool detects reality != desired and repairs. Drift: manual edits or changes outside the declared model. Imperative wins: cleanup scripts, one-off migrations, local setup, boot-time config where steps matter irreversibly.

Motivation

Why do you need it?

  • Declarative embraces change: an env repaired to spec after someone tweaks a port.
  • The plan (diff) is reviewable before any mutation — imperative scripts can't preview consequences.
  • Idempotency protects retries: CI re-running failing applies doesn't double-create.
  • Declarative intent survives tooling churn (rewrite engine, keep intent).
Think about it

Mental model

Imagine

Telling a chef 'serve chicken biryani' vs reciting the 40 recipe steps aloud

Think of

Declarative vs imperative

Declarative: chef converges to biryani and repairs if something's off. Imperative: you're responsible for step ordering and any deviation in the kitchen breaks the dish.

Mechanics

How it works

  • Declarative tools model resources in a graph and compute the diff.
  • Plans show add/change/destroy before touching anything.
  • Apply executes only the delta; state records the after.
  • Rerun plan → no delta ⇒ convergent — the promise of idempotency.
  • Non-declarative bits (scripts) still exist where ordering/irreversibility matters.

Two styles, same goal.

Imperative
Declarative
State diff
Converge
Run twice
Hands-on

Examples

Same change, both idioms

Same change, both idioms
# imperative (drift-prone)
aws ec2 run-instances ... ; aws ec2 authorize-security-group-ingress ...
# declarative (terraform)
resource "aws_instance" "web" { ami = "…"; instance_type = "t3.micro" }
resource "aws_security_group_rule" "web_8080" {
type = "ingress"; from_port = 8080; to_port = 8080
security_group_id = aws_security_group.web.id
}
# someone opens 9090 by hand → next apply closes the gap / alerts

The declarative form is a reviewable diff; the imperative form is a memo of actions that can drift.

Real world

In production

  • Terraform/Pulumi/CloudFormation for all provisioning: regions, roles, network, databases.
  • Helm/Jinja charts declare K8s desired state; kubectl apply converges.
  • Bash/docker-entrypoint for genuinely sequential, one-shot bootstrap steps.
Guidelines

Best practices

  • Default to declarative; reserve scripts for true bootstrapping edge cases.
  • Give every imperative step an idempotency guard (guard-if-exists checks).
  • Review plan diffs, not apply logs — that's where intent vs reality is decided.
  • Kill the habit of 'quick console edit'; make drift a hard alarm not a whoops.
Watch out

Common mistakes

  • Script that assumes clean state (create fails because 'already exists' on retry).
  • Declarative tool used imperatively (terraform apply -target everything, plan by ceremony).
  • Mixing styles without discipline: a manual change that future plans silently re-apply.
  • Believing declarative removes the need for state; it centralizes it.
When things break

Debugging tips

  • Apply timing out but reality exists → verify with query; often a permissions/state hiccup, not the resource.
  • Converge hammering a resource each run → attributes changing in a non-unique way (ForceNew).
  • Plan shows changes you didn't intend → last console edit is the smell; revert it or codify it.
  • Compare plan across runs to isolate what returns to the same diff over and over.
Interviews

Interview checkpoint

Test yourself

Quick quiz

0/2
1

A declarative tool's plan output enables:

2

You need idempotency primarily because:

Go deeper

Official documentation