Terraform modules are the building blocks of reusable infrastructure. Done well, they encode organisational standards once and apply them everywhere. Done badly, they become an unmaintainable tangle that's more work than writing infrastructure from scratch. This article documents the module design principles we apply across all client engagements.
The Single Responsibility Principle for Modules
A module should do one thing well. A module that provisions a "complete application environment" — VPC, subnets, security groups, compute, database, monitoring — is not a module; it's a programme. It's hard to test, hard to version, and impossible to reuse in contexts that need only part of its functionality.
We design modules at two levels: resource modules (thin wrappers around individual resource types with opinionated defaults) and composition modules (orchestrate multiple resource modules for a complete pattern, like a VPC with standard subnet layout). This separation makes each level independently testable and reusable.
Input Validation and Documentation
Every module input should have: a type constraint, a description, and validation rules where the value space is bounded. Undocumented inputs are unusable inputs — a module without variable descriptions forces every consumer to read the implementation to understand how to use it. We generate documentation automatically from variable and output descriptions using terraform-docs in CI.
Versioning and the Module Registry
Modules consumed without pinned versions are a maintenance nightmare. A version bump in a shared module can break consumers who didn't expect the change. We publish modules to a private Terraform registry (or a git tag-based release strategy) and require all consuming configurations to specify a version constraint. Major version bumps indicate breaking changes; patch versions are backward compatible.
The module registry also provides discoverability — engineers can search for existing modules before writing new infrastructure. We've reduced infrastructure duplication by 60% in organisations that moved from "engineers write whatever they need" to "engineers search the module registry first."
- Design modules at two levels: resource modules and composition modules
- Every input variable must have type, description, and validation constraints
- Use terraform-docs to generate documentation automatically in CI
- Pin module versions; use semantic versioning for breaking change communication
- A searchable module registry reduces infrastructure duplication significantly