Módulos como contratos
Otra VPC igual
El equipo de Notificacionesver en el mapa se muda a la nube y necesita una VPC como la de la app: su rango del plan de direcciones, dos subredes privadas, la ruta al hub y el adjunto al router de tránsito. Lo más rápido sería copiar el código de la app y cambiar los números. Marta sabe cómo acaba eso: dentro de un año habrá seis copias, cada una con sus arreglos, y ninguna igual a otra.
Así que la convierte en un módulo de plataforma: un trozo de código que otros usan como una pieza, con sus entradas y sus salidas. La primera versión sale enseguida. La pregunta difícil viene después: qué entradas tiene. Porque lo que el módulo enseña, cada equipo lo tiene que decidir, y lo que esconde, plataforma lo puede cambiar sin pedir permiso a nadie.
Plataforma y los equipos
Arriba de cada panel, el módulo de plataforma y lo que pide; debajo, los equipos que lo usan, cada uno con su código, su estado y la versión con la que trabaja. A la izquierda, un envoltorio fino: el módulo pasa al recurso sus 80 variables, y cada equipo las rellena. A la derecha, un módulo profundo: tres entradas (nombre, entorno y tamaño) y el módulo decide el resto a partir del plan de direcciones.
La app fija la versión (~> 1.4: cualquier 1.x desde la 1.4). Notificaciones, de partida, no la fija. Publica las dos versiones nuevas del módulo y mira qué recibe cada equipo en su siguiente plan; luego añade un tercer equipo.
Qué ha pasado
Lo que el módulo esconde, plataforma lo puede cambiar
Publica la 1.5, que activa los flow logs, el registro de todas las conexiones de la VPC, porque seguridad los pide en todas. A la derecha, los flow logs están detrás de la interfaz: los dos equipos los reciben en su siguiente plan, +1, sin tocar una línea. A la izquierda, el envoltorio fino no tiene dónde esconderlos. Son una variable más, flow_logs, y nadie los tendrá hasta que cada equipo la ponga a true en su código, abra una PR y alguien la revise. Dos equipos, dos PRs; con veinte equipos, veinte, y siempre habrá uno que no la haga.
Es la idea de módulo profundo que se usa en diseño de software: una interfaz pequeña que esconde mucho. El profundo pide tres cosas y decide el rango, el reparto en subredes, las rutas al hub y los registros. El fino pide 80 y no decide nada: no es una abstracción, es el recurso con otro nombre. Todo lo que el módulo enseña es algo que cada equipo tiene que entender, decidir y mantener, y algo que plataforma ya no puede cambiar sin romperles el código.
module "vpc" {
source = "registro.bcps/plataforma/vpc/nube"
version = "~> 1.4"
nombre = "notificaciones"
entorno = "pro"
tamaño = "pequeña"
}
Versiones: cada equipo actualiza cuando quiere
Un módulo compartido tiene el mismo problema que cualquier librería: si cambia, cambia para todos. La solución es la misma: versiones, con el significado habitual. Un cambio compatible (algo nuevo que no rompe a nadie) sube la versión menor: 1.4 a 1.5. Uno incompatible sube la mayor: 2.0. Cada equipo dice qué versiones acepta, y ~> 1.4 quiere decir «cualquier 1.x desde la 1.4»: recibe las mejoras y nunca una 2.0 sin pedirla.
El marcador lo cuenta como radio de impacto: el de una versión nueva son los equipos, y sus recursos, que la recibirán en su siguiente plan sin cambiar nada. Con la 1.5, todos. Con la 2.0, solo los que no han fijado la versión.
Cuándo hace daño: la versión sin fijar
Publica la 2.0, que reparte los rangos en subredes más grandes. Es una versión mayor precisamente porque no se puede aplicar sin más: las subredes cambian de rango, y el rango de una subred no se cambia en caliente. La app sigue en la 1.5 y ni se entera. Notificaciones no fijó la versión, así que su siguiente plan, lanzado por un cambio suyo que no tenía nada que ver, trae un -/+ sobre sus subredes. Si nadie lo lee, se reemplazan con todo lo que tengan dentro. Es el -/+ del episodio 2, esta vez llegado desde fuera del equipo.
A la izquierda, la 2.0 del envoltorio cambia el nombre de una variable, y el plan de Notificaciones falla: Unsupported argument. Es más ruidoso, pero es mejor fallo: no toca nada. En los dos casos, el remedio es el mismo, fijar la versión, y el daño viene de lo mismo: un contrato que cambia sin que el otro lado lo haya aceptado.
Cuándo hace daño: el envoltorio que no protege a nadie
Llega el equipo de Reportingver en el mapa. A la izquierda, copia las 80 líneas de la app para empezar, rango incluido: dos VPC con 10.64.1.0/24, y el día que las dos se enganchen al hub, el tráfico no sabrá a cuál ir. Es el rango solapado del primer episodio de redes, ahora con un módulo en medio que no lo ha evitado porque no decide nada. A la derecha, Reporting escribe tres líneas y el módulo le da el siguiente rango libre. Lo que el módulo esconde es también lo que ningún equipo puede hacer mal.
Un contrato entre dos equipos
Plataforma y los equipos de producto tienen una relación customer-supplier, en el vocabulario del context mapping de la serie de DDD: plataforma suministra, los equipos consumen, y lo que los consumidores necesitan pesa en lo que el proveedor hace. La interfaz del módulo, con sus versiones, es su published language: un contrato documentado, estable y versionado, que cada lado puede leer sin conocer las tripas del otro. Un envoltorio fino no es un published language: es exponer el modelo interno (el recurso de la nube) y obligar a todos a hablarlo.
Criterio
| Envoltorio fino | Módulo profundo | |
|---|---|---|
| Entradas | Todas las del recurso | Las que de verdad varían entre equipos |
| Un cambio de plataforma (un registro, una ruta) | Una PR por equipo | Una versión nueva; cada equipo la recibe al actualizar |
| Un equipo con un caso raro | Lo resuelve solo, con la variable que toque | Tiene que pedir una entrada nueva, o salirse del módulo |
| Errores posibles de cada equipo | Todos los del recurso | Los de tres entradas |
| Bueno para | Recursos que cada equipo configura a su manera; pocos equipos | Lo que tiene que ser igual en todos: red, permisos, registros |
El módulo profundo también hace daño si se pasa: cuando no deja hacer algo razonable, el equipo se lo salta, copia el código o vuelve a la consola, y el módulo deja de ser el camino. Una buena señal es cuántas veces piden los equipos una entrada nueva: si es a menudo, el módulo decide cosas que no le tocan.
-
¿Lo usan varios equipos y tiene que salir igual en todos?La red, los permisos base, los registros de auditoría.módulo profundo, con pocas entradas y versionado
-
¿Solo evita repetir código dentro de un equipo?Tres colas parecidas en la misma app.un módulo local, sin registro ni versiones
- El recurso directamente. Un envoltorio que solo pasa variables añade una capa sin quitar ninguna decisión. Y si se usa un módulo compartido: siempre con la versión fijada.
En el mundo real
Los módulos se publican en un registro: el público de Terraform, con módulos de la comunidad y de los proveedores, o uno privado dentro de HCP Terraform, GitLab o Artifactory. También se pueden usar directamente desde un repositorio, fijando la versión con una etiqueta de git (?ref=v1.4.0); sin etiqueta, se usa lo último de la rama, que es el caso de Notificaciones. El fichero .terraform.lock.hcl fija las versiones de los proveedores, no las de los módulos: esas las fija el propio version.
Muchos módulos populares de la comunidad son envoltorios bastante finos, con cientos de variables, porque intentan servir a todo el mundo; es razonable para un módulo público, que no sabe nada de quien lo usa. Dentro de una empresa, plataforma sí sabe: conoce el plan de direcciones, el hub y las reglas de seguridad, y puede decidir por los equipos. Herramientas como Renovate o Dependabot abren PRs solas cuando sale una versión nueva de un módulo, para que actualizar sea una decisión revisada y no un accidente.
Las pruebas de un módulo son las de cualquier librería: terraform test, desde la versión 1.6, ejecuta planes y applies de ejemplo contra el módulo y comprueba el resultado; Terratest hace lo mismo desde Go, creando infraestructura de verdad y destruyéndola al terminar.
Siguiente episodio
Con el módulo, cada equipo tiene su VPC igual que las demás. Pero todas viven en la misma cuenta de la nube, con los mismos permisos alrededor. El último episodio organiza toda la nube de BCPS Bank: una cuenta por equipo y entorno, la red hub compartida y reglas que valen para todos.
Episodio 6 La landing zone Cuentas por equipo y entorno, alta de cuentas por código y guardarraíles.← Todos los episodios · Episodio 4: Cuánto rompe un apply · Notificaciones en el mapa de BCPS Bank