We close the chapter on modules with a design question that distinguishes a good infrastructure engineer: should I make my modules very generic (usable for everything) or domain-specific (that solve exactly my case)? There is no single answer, but understanding the balance will help you design modules that truly make work easier, instead of complicating it.

Two Design Philosophies

When you create a module, you face a decision about how much flexibility to offer:

Generic Module

A generic module tries to serve many different cases. It exposes many input variables so that whoever uses it can configure everything. It is flexible and highly reusable.

"generic-server" module with 40 variables:
  type, operating system, disk, network, security,
  monitoring, backups, scaling, tags... etc.

Advantage: it works for almost any situation. Drawback: it is complex to use, because the caller has to understand and fill in many options, and can make mistakes.

Domain-Specific Module

A domain-specific module solves a specific case for your organization, with decisions already made. It exposes few variables because most things are set according to your company's conventions.

"company-web-server" module with 3 variables:
  name, environment, size
  (everything else is already decided according to company standards)

Advantage: very easy to use, and automatically applies your company's best practices. Drawback: it only works for that specific case; it is not reusable outside your context.

The Analogy: Watchmaker's Tool vs Swiss Army Knife

A generic module is like a complete professional toolbox: it has everything and works for any job, but you need to know which tool to pick and how to use it. A domain-specific module is like a kit ready for a specific task ("picture hanging kit"): it only brings what you need, with simple instructions, and solves that problem without you having to think. To hang a picture, the kit is more convenient; for varied jobs, you need the full toolbox.

The Problem with Being Too Generic

A very common beginner's mistake is trying to make everything super generic "just in case." The result is a module with dozens of variables that is as complicated to use as writing the resources by hand. You've added complexity without adding value.

Module with 50 variables "just in case":
  → so hard to configure that nobody wants to use it
  → the goal of modules (simplifying) is lost ⚠️

Golden rule: a module should hide complexity, not add it. If using your module is as hard as not using it, something is wrong with the design.

The Problem with Being Too Specific

The opposite extreme also has its risk: if you make modules so specific that each one serves only a single project, you end up with many modules that are almost identical and little real reuse. You lose part of the benefit of modules.

The Balance: The Practical Rule

The key is in the balance, and a good strategy is the two-layer approach:

Layer 1 — GENERIC Modules (base, reusable)
   "vpc-module", "server-module"  (flexible, few decisions made)
        │ used by...
        ▼
Layer 2 — Domain-SPECIFIC Modules (wrap the generic ones)
   "company-network", "company-web-server"
   (apply company conventions, easy to use)
        │ used by...
        ▼
   The teams of each project (who only pass 2-3 values)
  • Generic modules (often from the Registry, subchapter 18.3, or your own base modules) provide flexibility and reusable logic.
  • Domain-specific modules wrap them, setting your company's decisions (security, names, regions...) and exposing only what's necessary. These are what the teams use.

Real world example: a company's platform team uses the generic VPC module from the Registry (which has 40 options). But they don't force each team to deal with those 40 options. Instead, they create a specific company-network module that internally calls the generic one with all corporate decisions already made (ranges, tags, security), and only asks the user for a name and an environment. Teams get a perfect network by passing two values, and the flexibility of the generic module is still there underneath when needed.

Questions to Decide How to Design Your Module

When you create a module, ask yourself:

  • Who is going to use it? If it's many non-expert teams → go for specific and easy. If it's for the platform team → it can be more generic.
  • How many times and in how many contexts will it be used? Highly reused in varied contexts → more generic. For a specific case in your company → specific.
  • Am I hiding complexity or adding it? If you're adding complexity, rethink it.
  • Is every variable I expose really necessary? When in doubt, fewer variables: start simple and add flexibility only when it proves necessary.

What You Should Remember

  • When designing a module, you choose how much flexibility to offer: generic (many variables, works for a lot, but complex to use) or domain-specific (few variables, easy, but only for your case).
  • Like tools: the generic is the full toolbox (versatile but requires know-how); the specific is the kit for a task (convenient and straightforward).
  • Common mistake: being too generic "just in case" and creating a module so complicated that it doesn't simplify anything. A module should hide complexity, not add it.
  • Best strategy: two layers. Generic modules (base/Registry) that provide flexibility, wrapped by domain-specific modules that set company conventions and are easy to use.
  • When in doubt, start simple (few variables) and add flexibility only when it proves necessary.

You've finished Chapter 18! You now know how to design, version, and compose modules like a professional. In Chapter 19 we'll see how to manage multiple environments (development, staging, production) with Terraform: workspaces, directory strategies, and Terragrunt.

Cloud, AWS & Terraform — From Zero to Expert

Chapter 1 · What is cloud computing

Chapter 2 · The cloud market and major providers

Chapter 3 · Regions, availability zones and edge

Chapter 4 · Compute: EC2

Chapter 5 · Storage: S3

Chapter 6 · Networking: VPC

Chapter 7 · Identity and access: IAM

Chapter 8 · Managed databases

Chapter 9 · Why Infrastructure as Code

Chapter 10 · HCL: the Terraform language

Chapter 11 · Providers and state

Chapter 12 · Your first real infrastructure in Terraform

Chapter 13 · Load balancing and auto scaling

Chapter 14 · Serverless with Lambda

Chapter 15 · Messaging and events

Chapter 16 · Content delivery and DNS

Chapter 17 · Containers on AWS

Chapter 18 · Modules: reuse and composition

Chapter 19 · Workspaces and environment management

Chapter 20 · Remote backends and locking

Chapter 21 · Infrastructure testing

Chapter 22 · Terraform in CI/CD

Chapter 23 · Defense in depth

Chapter 24 · Observability: logs, metrics and traces

Chapter 25 · Cost optimization

Chapter 26 · High availability and disaster recovery

Chapter 27 · AWS Well-Architected Framework

Chapter 28 · Serverless architectures at scale

Chapter 29 · Data platforms on AWS

Chapter 30 · Multi-account and landing zones

Chapter 31 · Platform Engineering and Internal Developer Platform

Chapter 32 · Relevant AWS certifications

Chapter 33 · Projects to consolidate what you've learned

Chapter 34 · Resources and community

© Copyright 2024. All rights reserved