Most internal module libraries are abandoned within a year. The ones that survive share a design property: they hide decisions, not resources.
The failed module library follows a predictable arc. Someone wraps a cloud resource one-to-one, exposes every argument as a variable, and calls it reusable. Consumers immediately need an argument that was not exposed, so a variable gets added. Repeat for a year and you have a module with sixty variables that is harder to use than the resource it wraps.
The property that separates good from bad
A useful module encodes a decision your organisation has already made. Not 'here is a configurable storage account' but 'here is how we do storage: private endpoints on, public access off, diagnostics wired to the central workspace, retention set to policy'. The variables that remain are the ones that genuinely differ per use — name, size, environment.
Rules we hold to
- If a variable has only ever been set to one value, it should not be a variable
- Version modules and pin consumers to tags — a floating reference means every apply is a potential surprise
- Ship an example directory that runs, and test it in CI; an untested example is documentation that lies
- Outputs are API surface. Removing one is a breaking change and needs a major version
- Resist the meta-module. A module composing five other modules becomes the least debuggable object in the codebase
The test of a module library is whether a new service can reach production without its team reading the module source. If they have to read it, the abstraction has not earned its place.