Treat state as a production dependency
Remote state, locking, and narrow permissions are non-negotiable. A plan is a change proposal, not evidence that a change is safe.
Small modules win
Compose modules around stable domain concepts rather than cloud API objects. Inputs should describe intent; outputs should be intentionally few.
module "service_network" {
source = "./modules/service-network"
name = var.service_name
cidr = var.service_cidr
}
| Practice | Benefit |
|---|---|
| Version modules | reproducible change |
| Review plans | shared context |
| Policy checks | safer defaults |
References
- HashiCorp Terraform language documentation
A workflow teams can trust
Terraform becomes difficult when its state represents too many unrelated decisions. Split state along blast-radius and ownership boundaries: a shared network, a production application, and a disposable test environment should not require the same approval or have the same failure mode. Keep modules small, versioned, and documented around outcomes rather than a list of provider resources.
The review unit is the plan. Generate it in CI, retain it with the pull request, and apply the reviewed artifact rather than calculating a new one later. That practice closes a surprisingly common gap between what reviewers saw and what production received. Pair it with short-lived cloud credentials, policy checks, and a clear break-glass process for genuine incidents.
Questions for every change
- Who owns the resulting resource and its cost?
- Can this change be reversed safely?
- Does the state boundary expose unrelated environments?
- What policy would prevent the same unsafe configuration next time?
Further reading: HashiCorp recommended practices.