# Why Click-Ops Doesn't Scale

Before any tool, let's take a clear-eyed look at the thing IaC replaces - because the whole reason Terraform exists makes no sense until you've felt the pain it removes.

You open the cloud console. You click *Launch Instance*. You pick a size from a dropdown, choose a region, attach a security group, name it `web-1`, and hit go. A minute later you have a running server. It felt productive. It *was* productive - once.

The trouble starts the second time, and every time after.

## What "click-ops" actually is

📝 **Terminology.** *Click-ops* (sometimes *ClickOps*) is the informal name for managing infrastructure by hand through a web console's point-and-click interface - as opposed to defining it in code. Nobody named it as a compliment.

**What it actually is.** Click-ops is operating your cloud the way you operate a settings app: you navigate menus, fill in forms, and click buttons, and the cloud provider does what you asked *in that moment*. The result is a running resource. The *record* of how you got there is - nothing. The clicks evaporate the instant you make them.

**Why people start here.** It's the front door - every cloud console is designed to make the first server easy, because that's how they win you. For genuinely one-off exploration ("what does this service even do?"), clicking around is the right tool. The problem isn't that click-ops exists; it's what happens when it becomes how you *run* things.

Here's where it falls apart, and these three are worth naming clearly because each maps to a thing IaC fixes:

```text
   CLICK-OPS PROBLEM                     WHAT IT MEANS IN REAL LIFE
   ──────────────────────────────       ─────────────────────────────────────────
   Unrepeatable                   │     "Build staging exactly like prod" becomes
                                  │     an afternoon of clicking and guessing.
                                  │
   Undocumented                   │     The only record of your setup is the
                                  │     running thing itself. Lose it, lose the
                                  │     knowledge. No diff, no history, no review.
                                  │
   Drifts                         │     Someone clicks one "quick fix" at 2am.
                                  │     Now reality and everyone's mental model
                                  │     disagree, and no one knows.
```

### Unrepeatable

You set up `web-1` perfectly. Now your boss wants an identical staging copy, and a third one in another region for the EU launch. You're back in the console, retracing clicks from memory, hoping you pick the same instance size and the same security group rules and the same disk settings. You won't, exactly. Three servers that were *supposed* to be identical now differ in ways you'll discover at the worst possible time.

### Undocumented

Six months pass. The person who built the production network has left. A new hire asks, reasonably, "why is the database in this subnet and not that one?" There is no answer written down anywhere, because the decision was a click, and clicks don't leave notes. The infrastructure is its own and only documentation, and you can't `git log` a running server.

### Drift

This is the quiet killer. 📝 **Terminology.** *Drift* is when the real, running infrastructure no longer matches what anyone believes it to be - because someone changed it out-of-band. Production is slow one night, an engineer opens the console and bumps the server to a bigger size to get through the incident, and forgets to tell anyone or write it down. Now the live system silently disagrees with every diagram, every runbook, and every teammate's mental model. The next person who tries to "fix" something is working from a map that's wrong.

🪖 **War story.** A classic version: a team rebuilds staging from their notes, points the app at it, and half the features break. The cause: a single environment variable someone had clicked into the *old* staging months earlier to debug something, never removed, never documented. The notes were faithful - to a configuration that no longer existed. Days lost chasing a ghost left by a click.

## The mental shift: desired state, not steps

Here's the idea the entire rest of this guide stands on. It's a shift in *what you write down*.

**The click-ops way is imperative - you give steps.** "Launch an instance. Attach this disk. Open port 443. Set the name." You're a person performing a procedure, and the cloud follows along one click at a time. Stop halfway and you're left in a half-built state; a second copy means performing the whole procedure again.

**Infrastructure as Code is declarative - you describe the destination.** You write down *what should exist*: "one web server of this size, in this region, with port 443 open, named `web-1`." You do **not** write the steps to create it - you hand the description to a tool, and it figures out what to create, what to change, and what's already correct, to make reality match.

📝 **Terminology.** *Desired state* is that description: the complete picture of what your infrastructure *should* look like, written in files. *Declarative* means you specify the end state and let the tool work out how to reach it. *Imperative* means you specify the steps yourself. (You've met this split before - `git pull` is declarative-ish "make my branch match the remote," not "fetch object 1, then object 2.")

```mermaid
flowchart LR
  subgraph Imperative[Imperative: click-ops, scripts of steps]
    direction LR
    You1[you] --> S1[do step 1] --> S2[do step 2] --> S3[do step 3] --> Hope[hope]
  end
  subgraph Declarative[Declarative: Infrastructure as Code]
    direction LR
    You2[you] -->|this is what should exist| Tool[tool works out the steps and makes reality match]
  end
```

**Why this changes everything.** Once the desired state lives in a file:

- **It's repeatable.** Want a second identical environment? Point the tool at the same description. There's no "retracing clicks from memory" because there were never any clicks.
- **It's documented.** The file *is* the documentation, and it's the real, authoritative kind - because the tool enforces it. If the file says port 443 is open, port 443 is open.
- **It's reviewable.** The file goes in Git. Changes come as pull requests your teammates read before anything touches production. (This is exactly why [Git With Other People](/guides/git-with-other-people) pairs so naturally with IaC - the review muscle is the same one.)
- **Drift becomes visible.** Because the tool knows what *should* exist, it can compare that to what *does* exist and show you the difference. Drift stops being a silent ghost and becomes a line of output you can see and decide about.

⚠️ **The mindset gotcha that trips everyone.** Coming from click-ops, the instinct is to read a Terraform file as a *script that runs top to bottom*. It isn't. The order you write resources in mostly doesn't matter; Terraform reads the whole picture, figures out dependencies itself, and decides the order. Stop thinking "first do this, then that." Start thinking "here is the world I want; go make it so." Hold onto that and Phase 2 will feel obvious instead of strange.

## Recap

1. **Click-ops** - managing infrastructure by hand through a console - fails in three specific ways: it's **unrepeatable**, **undocumented**, and it **drifts**.
2. **Drift** is the dangerous one: reality silently diverges from what everyone believes, usually from an undocumented "quick fix."
3. **Infrastructure as Code** flips the model from **imperative** (you list the steps) to **declarative** (you describe the **desired state** and let a tool reach it).
4. Putting desired state in version-controlled files makes infrastructure **repeatable, documented, reviewable, and drift-visible** - the four things click-ops can never be.

Next: the tool itself. How Terraform turns a description into reality, and the one file you must respect to use it on a team.

Watch it animated: [infrastructure as code](/explainers/InfrastructureAsCode.dc.html)
