Oh Come TF On: The Terraform Friction Tax You Can’t Afford
Terraform is powerful, but its developer experience still creates too many 'oh come TF on' moments. Here’s where the friction comes from and how platform teams can fix it.
The reaction is universal
Every infrastructure engineer knows the feeling. You write a small Terraform change, run `terraform plan`, and then: a provider error, a state lock, a `for_each` type mismatch, or an HCL block that should work but doesn’t. The internal response is not polite. It is *oh come TF on*.
That reaction is not a developer being difficult. It is a signal that the tool just broke a mental model and charged the engineer for the privilege.
Terraform is powerful, not forgiving
Terraform’s declarative model is a genuine advantage. It gives teams a shared language for infrastructure and a repeatable plan/apply loop. But the developer experience still has sharp edges:
Plan errors often arrive late, after the author has left the context they were in.
HCL’s type system can surprise even experienced users when nested maps, conditionals, and `for_each` interact.
Provider behavior can drift between minor versions.
State files are easy to lock, hard to repair, and still require too much manual surgery.
Stack Overflow’s 2023 Developer Survey found that 62% of developers spend more than 30 minutes per day searching for answers, and 25% spend more than an hour. Terraform-specific friction adds another layer on top of that baseline.
Where the moments happen
1. Plan errors that tell you what, not why
An invalid index or a type mismatch in Terraform often points at a resource address without explaining the value that caused the failure. The engineer is left to reconstruct context from a wall of output. That is a fast way to trigger *oh come TF on*.
2. HCL’s expression trap
HCL looks simple, but it has surprising semantics. Dynamic blocks, `count` versus `for_each`, conditional expressions, and nested collection types can combine into errors that are technically correct but practically useless. The tool should not expect every user to become an HCL type expert before they can write a VPC module.
3. State is still a manual operation
State management should be boring. Instead, teams still write runbooks for `terraform state rm`, handle lock contention, and fix version mismatches. If your infrastructure changes require a state surgery guide, the tooling is asking too much.
4. Provider drift
Provider updates can change defaults, validation rules, or API calls. A minor provider bump should not create a wall of unexpected plan diffs. Without strict version pinning and migration notes, it often does.
Designing against the reaction
At Sapior, we treat *oh come TF on* as a bug report. The rule is simple: if a reasonable infrastructure engineer can hit a wall without a path forward, the tooling is broken.
That principle leads to concrete choices:
Predictable validation at authoring time, not only at plan time.
Error feedback that includes the resource, the value, and a concrete next step.
Provider version locks and migration notes for every breaking change.
State handling that is automatic, remote, and guarded by default.
One-command validation in CI before any change reaches production.
What platform teams can do today
If you run Terraform today, start with boring guardrails:
Pin provider and module versions.
Run `terraform validate` and `tflint` in CI.
Keep state remote and locked.
Avoid deeply dynamic HCL until there is a clear need.
Treat any error that takes more than five minutes to diagnose as a bug in your wrapper, docs, or defaults.
The payoff
Every removed *oh come TF on* moment reduces cognitive load, shortens review cycles, and makes infrastructure changes feel boring. That is the goal.
Developers recommend tools that make them feel competent. They abandon tools that make them feel stupid. The next time a plan fails with no clear path forward, do not blame the engineer. Fix the tool.