Choose a useful module boundary
A VPC module can reasonably own the network, subnets, routing, gateways, and common tags. It should not automatically absorb unrelated application resources simply because they connect to the network.
A clear boundary makes replacement, review, and security reasoning easier.
Expose decisions, not implementation noise
Inputs should describe choices a caller must understand: CIDR ranges, availability zones, public/private layout, NAT strategy, naming, and tags.
module "network" {
source = "./modules/network"
name = "education-platform"
vpc_cidr = "10.20.0.0/16"
availability_zones = ["ap-south-1a", "ap-south-1b"]
enable_nat_gateway = true
}
A module with dozens of obscure toggles may be flexible, but it often transfers complexity rather than removing it.
Make NAT cost visible
A managed NAT gateway provides a reliable path for private workloads, but it has hourly and data-processing cost. Small labs may choose a different design. Production systems may value the managed operational model. The module should not hide this trade-off behind an innocent default.
Security groups belong near workloads
The network module can provide shared boundaries, but workload-specific ingress should usually remain with the workload that understands the required traffic. This reduces a central file full of unrelated exceptions.
Return identifiers that support composition
Useful outputs include VPC ID, public and private subnet IDs, route-table IDs, and gateway details. Avoid returning internal values simply because they are available.
Plan output is not the final verification
Review the plan, apply through a controlled path, then verify actual routes, public exposure, DNS behavior, egress, and tagging. Automated policy checks can catch broad risks, but targeted connectivity tests answer whether the intended path works.
Version the module contract
Callers depend on input meaning and output shape. Breaking changes should be deliberate, documented, and tested against representative consumers.