Member-only story
Terraform Isn’t Application Code: Why Simplicity Beats ‘Cleverness’ in IaC
One of the most important (and overlooked) truths in infrastructure as code is this: Terraform is not application code.That might sound obvious, but I’ve seen countless developers — especially those coming from strong software engineering backgrounds — carry over habits from app development that don’t translate well to infrastructure.
And I get it. If you’re used to crafting elegant abstractions, designing highly modular systems, and refactoring for reusability, then seeing repeated code blocks in a Terraform repo can feel… wrong. You might feel compelled to “clean it up.” But before you do, let’s talk about why applying application development best practices to Terraform can backfire — and what to do instead.
The Appeal of Abstraction
In application development, abstraction is power. It gives us reusable components, cleaner logic, testability, and easier evolution over time. Object-oriented and functional paradigms thrive on abstraction.
So when we start writing Terraform, we reach for the same tools: modules, variables, reusable patterns. We refactor aggressively. We eliminate repetition.
But here’s the rub: infrastructure code has a very different job. Terraform doesn’t compile down into optimized machine instructions. It doesn’t need to be fast or efficient. It’s a model of your infrastructure, and the most important thing it can be is understandable.
“Other architectural aspects such as performance, efficiency, data integrity, and especially technical elegance are not important distinctions for Infrastructure as Code.”
Let that sink in. Terraform code doesn’t need to be clever — it needs to be boring. Predictable. Readable. Debuggable.
The Pitfalls of Over-Abstraction
Let me give you a real-world example. I’ve seen teams build elaborate module hierarchies to enforce consistency across cloud environments. They create a root module that wraps a child module that wraps a reusable “core” module with dozens of input variables — many of which are just passed straight through. On paper, this looks DRY and reusable. In practice? It’s a nightmare to debug.
When something breaks — or worse, silently misbehaves — you now have to:
- Track values across multiple layers of modules