$ 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.”