$ cat blog/terraform-modules-at-scale.md
Writing Terraform Modules That Scale
October 22, 2025 · 2 min read
Why Modules Matter
When your Terraform codebase grows beyond a single team, modules become the building blocks of your infrastructure. Done well, they accelerate teams. Done poorly, they become a bottleneck.
Here’s what I’ve learned building Terraform modules used by 10+ teams.
The Good Patterns
1. Opinionated Defaults, Flexible Overrides
variable "instance_type" {
description = "EC2 instance type"
type = string
default = "t3.medium" # sensible default
}
variable "tags" {
description = "Additional tags to apply"
type = map(string)
default = {}
}
locals {
default_tags = {
ManagedBy = "terraform"
Module = "ec2-instance"
Environment = var.environment
}
merged_tags = merge(local.default_tags, var.tags)
}
2. Versioned Releases
Always pin module versions. Use semantic versioning:
module "vpc" {
source = "git::https://github.com/org/tf-modules.git//vpc?ref=v2.1.0"
}
3. Minimal Required Variables
If a value can be computed or has a safe default, don’t require the consumer to set it. Every required variable is friction.
The Anti-Patterns
- God modules that try to do everything
- Leaky abstractions that expose every AWS API parameter
- No documentation or examples
- Breaking changes without version bumps
Testing
We use terratest for integration tests and terraform validate + tflint in CI:
$ terraform validate
$ tflint --module
$ go test -v ./tests/ -timeout 30m
Good modules are the difference between “infrastructure as code” and “infrastructure as spaghetti.”