Las cuatro lecciones anteriores han montado las piezas por separado: dónde vive el código, cómo se construye y se prueba, cómo se despliega sin cortar el servicio, y cómo se encadena todo. Esta lección no añade servicios nuevos. Coge un cambio real de Luis y lo sigue desde su portátil hasta producción, paso a paso, viendo en cada punto qué se comprueba y qué pasa exactamente si falla.
El cambio elegido no es trivial a propósito. Sara ha pedido añadir una franja horaria de entrega al
pedido, y eso toca cuatro cosas a la vez: el código de la tienda, un consumidor de
cola-mercadofresco-pedidos, el esquema de Aurora y el contrato del evento PedidoConfirmado. Es
exactamente el tipo de cambio que en el módulo 7 provocó un incidente —un productor desplegado antes que su
consumidor— y que aquí vamos a hacer sin que nadie escriba un comando ni cruce los dedos.
Aviso de coste. El recorrido completo de este cambio —cuatro ejecuciones del pipeline entre desarrollo, preproducción y producción, más las construcciones de las PR— cuesta unos 0,60 USD. AppConfig, que aparece al final, cobra 0,0000002 USD por petición de configuración con caché de 60 s: para MercadoFresco, céntimos al mes. Datos ficticios.
Contenido
- El cambio: franja horaria de entrega
- El recorrido completo, de un vistazo
- Rama, commits y pull request
- Construcción, pruebas y artefacto
- Compatibilidad de esquema: expansión y contracción en Aurora
- Compatibilidad de contrato: versionar
PedidoConfirmado - El orden correcto entre productor y consumidor
- Despliegue en desarrollo, humo y aprobación de Marta
- Canario en producción, vigilancia y promoción
- Configuración y secretos por entorno
- La pirámide de pruebas aplicada
- Métricas DORA: MercadoFresco antes y después
- Runbook de reversión y el fallo que se detecta tarde
- Despliegues pequeños y banderas de funcionalidad
- El viernes por la tarde como decisión de negocio
- Cierre del módulo y coste
- Errores comunes y consejos
- Ejercicios
- Conclusión
El cambio: franja horaria de entrega
Sara lo justifica con un dato: el 31 % de las incidencias de reparto son por ausencia del cliente. Cada
una cuesta un segundo intento, una llamada y, en producto fresco, a veces la mercancía. La petición es
añadir tres franjas —manana, tarde, 24h— al proceso de compra.
Lo que parece un campo de formulario toca cinco piezas:
| Pieza | Qué cambia | Riesgo |
|---|---|---|
Tienda web (asg-mercadofresco-tienda) |
Selector en el proceso de compra | Bajo |
| API de pedidos | Acepta y persiste franja_entrega |
Medio |
| Esquema de Aurora | Columna nueva en pedidos |
Alto: no revierte con el código |
Evento PedidoConfirmado |
Atributo nuevo en el detail |
Alto: hay consumidores ajenos |
| Consumidor de almacén | Lee la franja para ordenar las rutas | Medio |
Los dos riesgos altos son el corazón de la lección, y comparten una propiedad: son las dos cosas que la reversión automática de CodeDeploy no puede deshacer. Todo lo demás vuelve atrás en noventa segundos.
El recorrido completo, de un vistazo
flowchart TB
A[Luis: rama funcionalidad/franja-horaria] --> B[Push: pipeline sobre PR<br/>construye y prueba]
B -->|Rojo| B1[La PR no se puede fusionar]
B -->|Verde| C[PR 47: revision de Marta<br/>1 aprobacion obligatoria]
C --> D[Squash a desarrollo]
D --> E[Construccion: artefacto<br/>1.6.0-a3f9c21 inmutable]
E --> F[Calidad: estatico,<br/>contrato, secretos]
F --> G[Migracion EXPANDIR<br/>columna NULLABLE]
G --> H[Desplegar consumidor<br/>PRIMERO]
H --> I[Desplegar tienda en dev]
I --> J[Humo dev: flujo de compra]
J --> K[Preproduccion + humo]
K --> L[APROBACION MARTA<br/>version, diff, migracion]
L --> M[Produccion: blue/green<br/>+ canario en cobros]
M --> N[Puerta de calidad<br/>15 min de metricas]
N -->|OK| O[Promocion: etiqueta v1.6.0]
N -->|Degradado| P[REVERSION automatica<br/>90 s]
O --> Q[Semanas despues:<br/>CONTRAER]
Rama, commits y pull request
Luis parte de desarrollo, siguiendo el modelo de 08-01, y se impone el límite de tres días:
Los commits, con el formato convencional que el pipeline leerá después:
feat(pedidos): anadir franja_entrega al modelo y a la API feat(eventos): incluir franja_entrega en PedidoConfirmado como version 2 feat(almacen): ordenar rutas de reparto por franja test(pedidos): cubrir franjas validas, invalida y ausente docs(catalogo-eventos): documentar PedidoConfirmado v2
El primer feat determina que la versión suba a 1.6.0 —MINOR, funcionalidad compatible—. Si alguno
llevara BREAKING CHANGE: en el cuerpo, sería 2.0.0, y eso es justamente lo que vamos a evitar.
Al empujar la rama, el disparador de pull request de 08-04 arranca el pipeline en modo reducido:
construcción, pruebas y calidad, sin etapas de despliegue. El resultado se publica en la PR como
comprobación de estado, y la PR no se puede fusionar si está en rojo. Aquí se cierra por fin la
protección de main que quedó abierta en 08-01: no basta con que alguien apruebe, la construcción tiene que
estar en verde.
La PR #47 lleva la plantilla de 08-01, con una sección que en este cambio es la más importante:
## Riesgos
- Esquema Aurora: columna NULLABLE, sin DEFAULT, sin indice. Fase EXPANDIR.
NO revierte con el codigo. Contraccion prevista para dentro de 3 semanas.
- Evento PedidoConfirmado pasa a version 2: atributo OPCIONAL anadido.
Consumidores v1 lo ignoran. Verificado con pruebas de contrato.
- Orden de despliegue: consumidor de almacen ANTES que la tienda.Marta revisa y aprueba; Luis fusiona con squash, y el commit resultante entra en desarrollo.
Construcción, pruebas y artefacto
Al fusionar, el pipeline arranca completo. La etapa de construcción hace lo de 08-02 y produce
mercadofresco-tienda-1.6.0-a3f9c21.zip, con el commit en los metadatos.
Esto es lo único que se construye en todo el recorrido. El mismo objeto de S3 se desplegará en desarrollo, en preproducción y en producción. Si el pipeline reconstruyera antes de producción, la aprobación de Marta dejaría de significar nada, como vimos en el ejercicio 1 de 08-04.
| Comprobación | Bloquea | Qué pasa si falla |
|---|---|---|
ruff y formato |
Sí | Falla en 2 s; Luis corrige y vuelve a empujar |
git-secrets |
Sí | Falla y hay que rotar si el secreto era real |
| Pruebas unitarias (218) | Sí | La PR no se fusiona |
| Cobertura ≥ 75 % | Sí | Escribir las pruebas que faltan |
pip-audit |
Sí, con aviso | Actualizar la dependencia o documentar |
| Pruebas de contrato | Sí | La clave de este cambio; ver más abajo |
bandit |
No, informe | Se revisa los lunes |
Compatibilidad de esquema: expansión y contracción en Aurora
Aplicando la regla de 08-03, el cambio de esquema no va en el mismo paso que el código y se parte en tres fases separadas en el tiempo.
Fase 1, EXPANDIR, en una etapa propia del pipeline antes de cualquier despliegue de aplicación:
-- migraciones/2026-08-02-001-expandir-franja-entrega.sql
-- Compatible con la version 1.5.2, que no conoce esta columna.
ALTER TABLE pedidos ADD COLUMN franja_entrega VARCHAR(10) NULL;
-- Sin NOT NULL: la 1.5.2 inserta sin la columna y no falla.
-- Sin DEFAULT: no reescribe 4,2 millones de filas ni bloquea la tabla.
-- Sin indice: se anade en la fase de contraccion, con CONCURRENTLY.Los tres comentarios son el ejercicio entero. Con NOT NULL, la versión anterior fallaría en cada inserción
durante el despliegue gradual. Con DEFAULT, en PostgreSQL 11 y posteriores no se reescribe la tabla, pero
en motores anteriores sí, y sobre 4,2 millones de filas es un bloqueo de minutos. Y el índice se pospone
porque CREATE INDEX bloquea escrituras: se hará con CONCURRENTLY y fuera del despliegue.
Fase 2, MIGRAR, es el código 1.6.0: escribe franja_entrega cuando viene y tolera NULL cuando no.
Fase 3, CONTRAER, tres semanas después y como cambio propio, cuando ninguna instancia sirve ya la 1.5.2 y se ha comprobado que todos los pedidos nuevos traen franja:
CREATE INDEX CONCURRENTLY idx_pedidos_franja ON pedidos(franja_entrega);
UPDATE pedidos SET franja_entrega = '24h' WHERE franja_entrega IS NULL;
ALTER TABLE pedidos ALTER COLUMN franja_entrega SET NOT NULL;El control automático que impide saltarse esto es el análisis de migraciones en CodeBuild que
diseñamos en el ejercicio 3 de 08-03: falla la construcción si detecta DROP COLUMN, RENAME COLUMN,
SET NOT NULL o DROP TABLE en un fichero de migración sin la etiqueta -- CONTRACCION-APROBADA. La fase
3 la lleva y la fase 1 no la necesita.
Compatibilidad de contrato: versionar PedidoConfirmado
El otro riesgo alto. PedidoConfirmado lo consumen tres destinos de bus-mercadofresco: la cola del
almacén, la de analítica y la API del ERP del socio. Los dos últimos no los controla Luis, y ahí está el
problema: publicar un evento distinto puede romper a alguien que ni sabes que está escuchando.
| Cambio | ¿Compatible? | Qué hacer |
|---|---|---|
| Añadir un campo opcional | Sí | Subir la versión menor; adelante |
| Añadir un campo obligatorio | No | Tratarlo como opcional con valor por defecto |
| Quitar un campo | No | Deprecar, avisar, esperar, quitar |
| Renombrar un campo | No | Añadir el nuevo, publicar ambos, quitar el viejo |
| Cambiar el tipo de un campo | No | Campo nuevo con nombre distinto |
| Restringir valores permitidos | No | Ampliar, nunca restringir |
Añadir franja_entrega como campo opcional es el caso fácil, y el evento sube a versión 2:
{
"version": "2",
"source": "mercadofresco.tienda",
"detail-type": "PedidoConfirmado",
"detail": {
"id_pedido": "PED-2026-00841",
"id_cliente": "CLI-4471",
"importe_eur": 48.20,
"lineas": [{ "sku": "FRUT-0012", "uds": 3 }],
"franja_entrega": "tarde",
"confirmado_en": "2026-08-02T18:14:22Z"
}
}Un consumidor v1 recibe un objeto con un campo que no conoce y lo ignora, siempre que esté escrito con
la regla de oro: sé estricto con lo que publicas y tolerante con lo que recibes. Un consumidor que
valide el esquema con additionalProperties: false se rompería, y por eso la prueba de contrato es
bloqueante:
# tests/contrato/test_pedido_confirmado.py
import pytest
from app.eventos import construir_evento_pedido_confirmado
from consumidores.almacen_v1 import procesar as procesar_v1 # el consumidor ANTIGUO
from consumidores.almacen_v2 import procesar as procesar_v2
PEDIDO = {"id_pedido": "PED-2026-00841", "id_cliente": "CLI-4471",
"importe_eur": 48.20, "lineas": [{"sku": "FRUT-0012", "uds": 3}],
"franja_entrega": "tarde"}
def test_consumidor_ANTIGUO_no_se_rompe_con_el_evento_NUEVO():
"""Compatibilidad hacia delante: v1 debe ignorar el campo que no conoce."""
r = procesar_v1(construir_evento_pedido_confirmado(PEDIDO))
assert r["estado"] == "procesado" # no lanza, no rechaza
def test_consumidor_NUEVO_tolera_el_evento_ANTIGUO():
"""Compatibilidad hacia atras: durante el despliegue habra eventos v1 en vuelo."""
sin_franja = {k: v for k, v in PEDIDO.items() if k != "franja_entrega"}
ev = construir_evento_pedido_confirmado(sin_franja)
r = procesar_v2(ev)
assert r["estado"] == "procesado"
assert r["franja_asignada"] == "24h" # valor por defecto explicito
def test_franja_invalida_se_rechaza_antes_de_publicar(bus_falso):
with pytest.raises(ValueError, match="franja_entrega"):
construir_evento_pedido_confirmado({**PEDIDO, "franja_entrega": "noche"})
assert bus_falso.eventos_publicados == 0Las dos pruebas centrales son la segunda y la tercera, y conviene entender por qué se necesitan las dos. La segunda comprueba compatibilidad hacia delante: el consumidor viejo con el evento nuevo, que es lo que ocurre en cuanto la tienda empieza a publicar la v2 y todavía queda algún consumidor sin actualizar. La tercera comprueba compatibilidad hacia atrás: el consumidor nuevo con el evento viejo, que es lo que ocurre con los mensajes que ya estaban en la cola cuando se desplegó el consumidor. Durante un despliegue gradual las dos situaciones existen a la vez, y una prueba sola solo cubre la mitad.
Para el ERP del socio, que no se puede probar, la salvaguarda es distinta: el archivo de EventBridge que activamos en 07-03. Si el socio se rompe, hay 90 días de eventos para reproducir.
El orden correcto entre productor y consumidor
Aquí se cierra el incidente que quedó planteado en el módulo 7. La regla es corta y vale para cualquier cambio de contrato:
Despliega siempre primero el que lee, después el que escribe.
El razonamiento es de teoría de conjuntos. Un consumidor nuevo entiende los eventos viejos y los nuevos
—porque lo hemos probado—, así que desplegarlo primero es seguro: durante la ventana solo hay eventos
viejos, que sabe procesar. Al revés no funciona: si el productor sale primero, hay eventos v2 circulando
hacia consumidores v1 durante los minutos que dure el despliegue, y basta con que uno valide estrictamente
para que aparezcan mensajes en mercadofresco-pedidos-fallidos.
En el pipeline esto es una etapa con dos acciones y runOrder distinto, que es exactamente el mecanismo de
08-04:
{
"name": "DesplegarProduccion",
"actions": [
{ "name": "ConsumidorAlmacen", "runOrder": 1,
"configuration": { "ApplicationName": "app-mercadofresco-trabajadores",
"DeploymentGroupName": "dg-mercadofresco-trabajadores-produccion" },
"inputArtifacts": [{ "name": "PaqueteTienda" }] },
{ "name": "TiendaWeb", "runOrder": 2,
"configuration": { "ApplicationName": "app-mercadofresco-tienda",
"DeploymentGroupName": "dg-mercadofresco-tienda-produccion" },
"inputArtifacts": [{ "name": "PaqueteTienda" }] }
]
}runOrder: 1 para el consumidor y 2 para la tienda. El orden deja de depender de que alguien se
acuerde: está declarado, versionado y se ejecuta igual todas las veces. Ese es, en una frase, el valor de
todo el módulo 8.
Despliegue en desarrollo y pruebas de humo
En desarrollo el pipeline usa AllAtOnce porque la velocidad importa más que la disponibilidad. Las pruebas
de humo de 08-04 corren contra el entorno recién desplegado y añaden dos casos de este cambio:
def test_pedido_con_franja_se_confirma_y_persiste():
r = _crear_pedido({"franja_entrega": "tarde"})
assert r.status_code == 201
assert r.json()["franja_entrega"] == "tarde"
assert r.elapsed.total_seconds() < 2.0
def test_pedido_SIN_franja_sigue_funcionando():
"""Compatibilidad hacia atras contra el entorno real, no simulado."""
r = _crear_pedido({})
assert r.status_code == 201
assert r.json()["franja_entrega"] in (None, "24h")La segunda prueba es la que de verdad importa: verifica contra un entorno real que un cliente con la app antigua en el móvil sigue pudiendo comprar. Si falla, el pipeline se detiene en desarrollo y nadie se ha enterado fuera del equipo.
La aprobación de Marta
A las 10:42 le llega la notificación a alertas-mercadofresco y a Slack:
Pipeline: pipeline-mercadofresco-tienda | Ejecucion: 7a1f2c33 Version 1.6.0-a3f9c21 | Autor: luis | PR #47 Humo preproduccion: OK (12/12) | Latencia p95 preprod: 380 ms MIGRACION: fase EXPANDIR aplicada (columna NULLABLE, no revierte con el codigo) EVENTO: PedidoConfirmado pasa a v2 (atributo opcional) Ventana: OK (martes 10:42, fuera de la franja prohibida) Diff: github.com/mercadofresco/mercadofresco-tienda/compare/v1.5.2...1.6.0-a3f9c21
Marta abre el diff, ve 214 líneas en cuatro ficheros, comprueba que la migración es de expansión y aprueba. Cuarenta segundos. No porque confíe a ciegas, sino porque el mensaje responde por adelantado a las preguntas que se haría: qué entra, quién lo hizo, si se probó, qué no revierte y si es buen momento.
Canario en producción y vigilancia
La etapa de producción hace tres despliegues coordinados:
| Orden | Qué | Estrategia | Duración |
|---|---|---|---|
| 1 | Consumidor de almacén | En el sitio, AllAtOnce |
~2 min |
| 2 | Lambda mercadofresco-cobrar-pago |
Canary10Percent5Minutes |
~5 min |
| 3 | Tienda web | Blue/green, espera de 30 min | ~12 min |
Durante el proceso vigilan tres alarmas conectadas a la reversión automática:
mercadofresco-alb-latencia-alta, mercadofresco-pedidos-fallidos y
mercadofresco-cobrar-pago-errores-canario. Y al terminar, la puerta de calidad de 08-04 observa 15
minutos las métricas de MercadoFresco/Tienda, con la ventana dimensionada para caber dentro de los 30
minutos de terminationWaitTimeInMinutes —si la puerta tardara más, el entorno azul ya estaría terminado y
la reversión de 90 segundos habría dejado de existir—.
Promoción o reversión automática
Si todo va bien, el pipeline termina, CodeDeploy termina el entorno azul a los 30 minutos y una acción final etiqueta la versión:
Lo que se etiqueta es lo que llegó a producción y sobrevivió a la puerta de calidad, no lo que se construyó. Es una diferencia importante: la etiqueta certifica un hecho, no una intención.
Si algo falla, cada mecanismo actúa en su plazo:
| Fallo | Quién lo detecta | Reacción | Tiempo |
|---|---|---|---|
| Errores en el canario | Alarma del alias | CodeDeploy devuelve el peso | Segundos |
| Latencia alta tras el cambio de tráfico | mercadofresco-alb-latencia-alta |
Oyente vuelve al azul | ~90 s |
| Mensajes en la DLQ | mercadofresco-pedidos-fallidos |
Reversión + aviso | ~2 min |
| Degradación sutil sin alarma | Puerta de calidad | Detiene el pipeline | ~15 min |
| Nada de lo anterior | Una persona | Runbook | Minutos u horas |
Y la parte que ningún mecanismo revierte: la columna franja_entrega sigue ahí. No pasa nada, porque
es NULLABLE y la 1.5.2 la ignora. Ese es exactamente el motivo de que la fase de expansión sea inocua:
convierte un cambio irreversible en uno que no molesta.
Configuración y secretos por entorno
El artefacto es el mismo en los tres entornos, así que la diferencia entre ellos no puede estar en el paquete: está en la configuración que se lee al arrancar, con lo que montamos en 04-03.
aws ssm put-parameter --name /mercadofresco/produccion/franjas_entrega_activas \
--value "manana,tarde,24h" --type String --overwrite \
--profile mercadofresco-dev --region eu-west-1| Tipo de valor | Dónde vive | Ejemplo |
|---|---|---|
| Configuración no sensible | Parameter Store /mercadofresco/<entorno>/ |
Franjas activas, umbrales |
| Secretos | Secrets Manager, con rotación | mercadofresco/produccion/rds/mfadmin |
| Constantes del código | En el repositorio | Formato de fecha, códigos de error |
| Valores «a mano» | En ningún sitio | — |
La última fila es la regla: si un valor difiere entre entornos, tiene que estar en Parameter Store. Un valor puesto a mano en una instancia desaparece en el siguiente despliegue blue/green —las instancias son nuevas— y reaparece como un fallo intermitente imposible de diagnosticar.
La pirámide de pruebas aplicada
| Nivel | Cuántas | Dónde corren | Duración | ¿Bloquea? |
|---|---|---|---|---|
| Unitarias | 218 | CodeBuild, sin red | 45 s | Sí |
| Integración | 34 | CodeBuild con moto |
1 min | Sí |
| Contrato | 12 | CodeBuild | 15 s | Sí |
| Humo | 12 | Contra dev, preprod y prod | 40 s | Sí |
| Carga | 1 | Preproducción, nocturna | 20 min | No: informe |
La forma de la pirámide no es estética: la base es ancha porque las pruebas de abajo son baratas, rápidas y precisas, y la cima es estrecha porque las de arriba son lentas, caras y frágiles. Un equipo con 200 pruebas de extremo a extremo y 20 unitarias tiene una suite que tarda una hora, falla de forma intermitente y no dice dónde está el problema.
Las pruebas de contrato son el nivel que casi nadie tiene y el que este cambio demuestra necesario: cuestan 15 segundos y son lo único que detecta que un consumidor ajeno se va a romper.
Métricas DORA: MercadoFresco antes y después
Las cuatro métricas DORA miden la capacidad de entrega de un equipo. Las de MercadoFresco, medidas sobre los tres meses anteriores al módulo 8 y sobre los dos posteriores:
| Métrica | Antes (mayo-julio) | Después (agosto-septiembre) | Categoría |
|---|---|---|---|
| Frecuencia de despliegue | 1,2 por semana | 4,8 por semana | De media a alta |
| Tiempo de entrega (commit → producción) | 6 días y 4 h | 3 h 20 min | De media a alta |
| Tasa de fallo de cambios | 23 % | 7 % | De baja a alta |
| Tiempo de restauración | 47 min (mediana) | 4 min | De media a élite |
Tres lecturas que conviene hacer sobre estos números, porque las cifras solas engañan:
La frecuencia sube porque el coste marginal de desplegar baja. Antes, desplegar costaba a Luis una hora de trabajo y una dosis de nervios, así que acumulaba cambios: «ya que despliego, que vayan los cuatro». Ahora cuesta cero, así que despliega en cuanto algo está listo. Y eso realimenta la tasa de fallo: un despliegue de un cambio es mucho más fácil de diagnosticar que uno de cuatro.
El tiempo de restauración es la mejora más grande y la más importante. De 47 minutos a 4 no es una mejora de proceso, es un cambio de naturaleza: 47 minutos de tienda degradada un viernes a las 19:00 son unos 700 pedidos afectados; 4 minutos son unos 60. Y la mayor parte de esos 4 minutos es detección, no reacción, porque la reversión en sí tarda 90 segundos.
La tasa de fallo del 7 % no es cero y no debería serlo. Un equipo con 0 % de fallos casi siempre está desplegando demasiado poco y demasiado tarde. El objetivo no es no fallar nunca, es que fallar sea barato: que un fallo cueste 4 minutos y no 47.
Y una advertencia. Las métricas DORA son útiles como termómetro del equipo y peligrosas como objetivo individual: en cuanto «frecuencia de despliegue» pasa a ser una meta, aparecen despliegues vacíos para subir el número. Mídelas, mira la tendencia, y no las cuelgues en un tablero de rendimiento personal.
Runbook de reversión y el fallo que se detecta tarde
Los mecanismos automáticos cubren los primeros quince minutos. El caso difícil es el fallo que aparece a las 19:30 de un despliegue de las 11:00, cuando el entorno azul ya no existe y la puerta de calidad dio el visto bueno hace horas.
Este es el runbook, pensado para seguirse a las tres de la mañana:
1. Contener (0-5 min). ¿Se puede apagar sin desplegar? Si el cambio está detrás de una bandera de
funcionalidad, desactivarla es cuestión de segundos y es siempre la primera opción. Si no, deshabilitar la
transición del pipeline hacia producción con disable-stage-transition para que nadie empeore la
situación mientras se diagnostica.
2. Decidir la dirección (5-10 min). Es la decisión que más se equivoca bajo presión:
| Situación | Dirección | Por qué |
|---|---|---|
| Solo cambió el código | Atrás: desplegar la versión anterior | Rápido y seguro |
| Hubo migración de expansión | Atrás: la columna sobra pero no molesta | Inocua por diseño |
| Hubo migración de contracción | Adelante: corregir y desplegar | Volver atrás rompería más |
| El evento subió de versión | Depende de si hay consumidores actualizados | Ver más abajo |
3. Ejecutar (10-20 min). Hacia atrás es un despliegue normal de la versión anterior por el pipeline,
usando el artefacto que sigue en S3 con su ruta predecible —tienda/1.5.2-8b2e440/paquete.zip—, lo que
hace la reversión posible en un minuto. Es la razón de que la regla de ciclo de vida del bucket sea de 60
días y no de 3.
4. Verificar (20-30 min). /salud devuelve la versión esperada, TiempoConfirmacionPedido vuelve a su
franja normal, PedidosConfirmados recupera el ritmo, y la DLQ deja de crecer.
5. Recuperar los datos, el paso que se olvida y el que más daño deja. Los mensajes acumulados en
mercadofresco-pedidos-fallidos se procesan con el runbook de la DLQ de 07-05 —contener, clasificar,
corregir, reprocesar—, nunca con un redrive antes de corregir la causa. Y si el evento había subido de
versión, lo publicado durante el incidente se reproduce desde el archivo de EventBridge, siempre con
FilterArns para no repetir efectos que ya ocurrieron.
6. Post-mortem sin culpables, en 48 horas. Qué pasó, por qué las puertas no lo detectaron y qué prueba nueva lo habría detectado. Ese último punto es el único entregable obligatorio: un post-mortem que no produce una prueba nueva es una reunión.
Despliegues pequeños y banderas de funcionalidad
Dos prácticas que multiplican el valor de todo lo anterior.
Despliegues pequeños y frecuentes. No es una preferencia estética: es aritmética del diagnóstico. Un despliegue con un cambio tiene un sospechoso; uno con seis tiene quince parejas posibles de interacción. La regla de MercadoFresco es que una rama vive tres días como máximo —de 08-01— y que si un cambio no cabe, se parte en incrementos compatibles hacia atrás, exactamente como se ha partido este.
Banderas de funcionalidad con AWS AppConfig. Separan desplegar de publicar: el código sale a producción apagado, y se enciende cuando y para quien se decida.
# app/config.py
import boto3, json, time
cliente = boto3.client("appconfigdata")
_cache, _expira, _token = {}, 0, None
def bandera(nombre, por_defecto=False):
"""Lee AppConfig con cache de 60 s. Ante error, devuelve el valor por defecto."""
global _cache, _expira, _token
try:
if time.time() > _expira:
if _token is None:
_token = cliente.start_configuration_session(
ApplicationIdentifier="mercadofresco",
EnvironmentIdentifier="produccion",
ConfigurationProfileIdentifier="banderas",
RequiredMinimumPollIntervalInSeconds=60)["InitialConfigurationToken"]
r = cliente.get_latest_configuration(ConfigurationToken=_token)
_token = r["NextPollConfigurationToken"]
contenido = r["Configuration"].read()
if contenido:
_cache = json.loads(contenido)
_expira = time.time() + 60
return _cache.get(nombre, {}).get("enabled", por_defecto)
except Exception:
return por_defecto # una bandera rota NUNCA debe tumbar la tiendaCon esto, el proceso de compra hace if bandera("franja_entrega_activa"): y Luis despliega el martes con la
bandera apagada, la enciende el miércoles al 10 % de clientes, mira las métricas y llega al 100 % el
jueves. Desplegar deja de ser el momento de riesgo.
| Aspecto | Despliegue canario | Bandera de funcionalidad |
|---|---|---|
| Qué controla | Qué versión del código corre | Qué comportamiento se activa |
| Granularidad | % de peticiones | Por cliente, región o segmento |
| Tiempo de apagado | 90 s (reversión) | Segundos, sin desplegar |
| Duración razonable | Minutos | Días o semanas |
| Coste si se abusa | Ninguno | Deuda técnica: ramas muertas en el código |
La última fila es la advertencia. Una bandera es código temporal con fecha de caducidad: hay que apuntarla en el backlog el mismo día que se crea y borrarla en cuanto esté al 100 % y estable. Un sistema con cuarenta banderas antiguas tiene un espacio de estados que nadie puede razonar ni probar.
El viernes por la tarde como decisión de negocio
«No se despliega en viernes» es una de las reglas más repetidas del sector, y casi siempre por el motivo equivocado. El motivo habitual es el miedo: no sabemos qué pasará y no queremos estar aquí para verlo. Eso no es una política, es un síntoma —de que el pipeline no da confianza, de que la reversión no está probada, de que nadie sabe cuánto se tarda en volver atrás—.
Con lo que MercadoFresco tiene ahora, la pregunta se puede responder con números:
| Factor | Lunes 10:00 | Viernes 19:00 |
|---|---|---|
| Pedidos/hora | ~180 | ~900 |
| Pedidos afectados por 4 min de degradación | ~12 | ~60 |
| Personas disponibles para responder | 3 | 1 |
| Tiempo hasta la siguiente ventana | 1 h | 60 h |
La regla de MercadoFresco no es «no se despliega en viernes», es «no se despliega a producción entre las 16:00 y las 22:00 de los viernes». Y la razón no es el miedo: es que el mismo incidente cuesta cinco veces más en esa franja y hay un tercio del equipo para atenderlo. Es una decisión de negocio, tomada con datos, revisable si los datos cambian. Fuera de esa franja se despliega el viernes como cualquier otro día, incluida la tarde del jueves y la mañana del viernes.
Y se implementa como control, no como recordatorio: una transición deshabilitada de forma programada, para que no dependa de que alguien se acuerde a las 18:50.
Cierre del módulo y coste
El cuarto problema del curso queda resuelto. El módulo empezó con el código de MercadoFresco en un
portátil, un scp por SSH un viernes por la tarde y ninguna forma de volver atrás salvo buscar el fichero
anterior. Termina con un cambio que toca cuatro sistemas recorriendo el camino completo sin que nadie
escriba un comando.
| Problema del curso | Módulo | Estado |
|---|---|---|
| 1. Servidor único que no aguanta el pico | 2-3 | Resuelto: ASG, ALB, CloudFront |
| 2. Seguridad y secretos | 4 | Resuelto: IAM, KMS, Secrets Manager, WAF |
| 3. Confirmación de pedido de 2.893 ms | 6-7 | Resuelto: 400 ms con colas y eventos |
| 4. Despliegues arriesgados | 8 | Resuelto |
| 5. Infraestructura creada a mano | 9 | Pendiente |
La cadena completa, en una frase por eslabón: 08-01 puso el código en un repositorio con ramas cortas,
pull requests y protección de main. 08-02 convirtió cada push en una construcción limpia que prueba,
escanea y produce un artefacto inmutable. 08-03 convirtió el despliegue en una operación gobernada con
ganchos, estrategias graduales y reversión automática ante alarma. 08-04 encadenó todo en un pipeline
con etapas, aprobación informada y puertas de calidad. Y 08-05 ha demostrado que la cadena aguanta un
cambio de verdad, con esquema y contrato incluidos.
| Concepto | Coste mensual |
|---|---|
| CodeCommit / GitHub / CodeConnections / AppConfig | 0,10 USD |
| CodeBuild (~1.850 min) | 20,90 USD |
| CodeDeploy (capacidad blue/green) | 2,00 USD |
| CodePipeline V2 | 3,50 USD |
| Artefactos en S3 y registros | 1,30 USD |
| Total del módulo 8 | ≈ 27,80 USD/mes |
Veintiocho dólares al mes. La comparación correcta no es contra cero: es contra 47 minutos de mediana de restauración, un 23 % de despliegues que fallaban y las horas que Luis dedicaba a desplegar a mano.
Errores Comunes y Consejos
Desplegar el productor antes que el consumidor. Es el incidente del módulo 7 y la regla es una sola frase: primero el que lee, después el que escribe.
Meter el cambio de esquema en el mismo despliegue que el código. La reversión revierte artefactos, no bases de datos.
Una migración de expansión con NOT NULL o con índice. Deja de ser inocua: rompe la versión anterior o
bloquea la tabla.
Probar solo la compatibilidad hacia atrás. Durante un despliegue gradual existen las dos direcciones a la vez; hacen falta las dos pruebas.
Quitar o renombrar un campo de un evento. No es compatible aunque el consumidor «parezca» tolerante. Añadir, publicar ambos, esperar, quitar.
Reconstruir el artefacto antes de producción. Invalida todas las pruebas y la aprobación anteriores.
Configuración distinta puesta a mano en una instancia. Desaparece en el siguiente blue/green y vuelve como un fallo intermitente indiagnosticable.
Banderas de funcionalidad que nunca se retiran. Cuarenta banderas antiguas son un espacio de estados que nadie puede probar. Apúntala en el backlog el día que la creas.
Una bandera que tumba la tienda si AppConfig falla. El valor por defecto ante error debe ser siempre seguro.
Usar las métricas DORA como objetivo individual. Aparecen despliegues vacíos y la métrica deja de medir nada.
Redrive de la DLQ antes de corregir la causa. Los mensajes vuelven a fallar y se pierde información.
Consejo: escribe la sección «Riesgos» de la PR antes de escribir el código. Si no sabes qué riesgos tiene tu cambio, todavía no lo has diseñado. Y ensaya la reversión a propósito una vez al trimestre, cronometrándola: es la única forma de que el número del runbook sea cierto.
Consejo: en el post-mortem, el entregable obligatorio es una prueba nueva. Sin eso, es una reunión.
Consejo: convierte «no desplegamos los viernes» en una ventana con datos. Una regla que se justifica con números se respeta; una que se justifica con miedo se salta el día que hay prisa.
Ejercicios
Ejercicio 1: el cambio que no cabe en tres días
Sara pide ahora algo mayor: entregas programadas hasta con 7 días de antelación, con calendario de
disponibilidad por código postal, precios distintos según el día y un límite de pedidos por franja y zona.
Toca la tienda, la API, tres tablas nuevas en Aurora, el consumidor de almacén, el evento
PedidoConfirmado (que necesita fecha_entrega además de franja_entrega) y un cálculo de precio nuevo.
Luis estima tres semanas.
Descomponlo en incrementos que respeten el límite de tres días por rama. Para cada uno indica qué se despliega, qué cambia en el esquema y en qué fase, si necesita bandera de funcionalidad y qué se puede verificar en producción al terminarlo. Di explícitamente cuál es el primero y por qué.
Ejercicio 2: el fallo de las 19:30
Martes, 11:00: se despliega la 1.6.0 con todo en verde. A las 19:30, Sara avisa de que en el panel
mercadofresco-negocio los pedidos con franja tarde han caído a cero desde las 17:00, mientras que
manana y 24h van normales. PedidosConfirmados total está solo un 8 % por debajo de lo normal, ninguna
alarma ha saltado y la puerta de calidad dio el visto bueno a las 11:20. El entorno azul ya no existe.
Responde: (a) por qué ningún mecanismo automático lo detectó; (b) los primeros 15 minutos, en orden; (c) hacia dónde revertir y por qué, sabiendo que la migración fue de expansión; (d) qué se hace con los pedidos de esas dos horas y media; (e) qué tres controles nuevos saldrían del post-mortem.
Ejercicio 3: medir y mejorar DORA
MercadoFresco lleva dos meses con el pipeline. Datos del último mes: 19 despliegues a producción; tiempo de entrega mediano de 3 h 20 min, del que 2 h 40 min es espera de la aprobación de Marta; 2 despliegues revertidos de 19; tiempo de restauración mediano de 4 min, pero con un caso de 95 minutos —el fallo detectado tarde—. Marta quiere llegar a categoría élite en las cuatro métricas.
Para cada una: di dónde está MercadoFresco, cuál es el objetivo élite, qué cambio concreto propondrías y qué riesgo tiene ese cambio. Indica cuál abordarías primero y justifícalo.
Soluciones
Solución 1
Seis incrementos, cada uno desplegable y verificable por separado. El principio que los ordena es que cada incremento debe ser útil o inocuo por sí solo, nunca «media funcionalidad a medio camino».
| # | Qué se despliega | Esquema | Bandera | Verificable al terminar |
|---|---|---|---|---|
| 1 | Tablas de calendario y disponibilidad, vacías; nadie las lee | EXPANDIR: 3 tablas nuevas | No | Las tablas existen y no afectan a nada |
| 2 | Carga del calendario y API interna de consulta | Ninguno | No | /interno/disponibilidad?cp=08001 responde |
| 3 | fecha_entrega en pedido y evento (v3), opcional |
EXPANDIR: columna NULLABLE |
Sí, apagada | Pedidos siguen funcionando sin fecha |
| 4 | Consumidor de almacén lee fecha_entrega con respaldo |
Ninguno | No | Procesa eventos con y sin fecha |
| 5 | Selector de fecha en el proceso de compra | Ninguno | Sí, al 10 % | Un 10 % de clientes ve el calendario |
| 6 | Precio por día y límite por franja y zona | EXPANDIR: columna de precio | Sí | Precios correctos con la bandera activa |
El primero es el incremento 1, y la razón es la regla de esta lección: el cambio de esquema es lo único que no revierte con el código, así que va solo, en su propio despliegue, sin nada más que pueda fallar y obligar a una reversión. Tres tablas vacías que nadie consulta son el cambio más seguro que existe: si algo va mal, no hay nada que deshacer. Meterlas en el mismo despliegue que el código nuevo sería mezclar el riesgo irreversible con el reversible, que es justo lo que 08-03 advierte.
Dos observaciones más. El incremento 4 va antes que el 5 por la regla del orden: el consumidor entiende antes de que el productor empiece a publicar. Y el 3 despliega el evento v3 con la bandera apagada, de modo que el campo existe en el código pero no se publica hasta que se encienda: eso da una ventana para verificar los consumidores en producción sin haber cambiado nada de verdad.
Solución 2
(a) Porque ninguna alarma medía la dimensión que falló. PedidosConfirmados es un agregado, y una
caída del 8 % en el total está dentro del ruido normal de un martes; la alarma de latencia no salta porque
el sistema no está lento, está rechazando; la DLQ no crece porque probablemente no hay excepción, sino una
validación que devuelve un 400 limpio. Y la puerta de calidad hizo su trabajo correctamente a las 11:20,
seis horas antes de que el problema apareciera: era una franja horaria con poco tráfico de tipo tarde.
La lección de fondo es incómoda y vale para cualquier sistema: las puertas automáticas detectan lo que se
les ha dicho que miren, en la ventana en la que miran. Un fallo que solo afecta a un segmento y solo se
manifiesta seis horas después es invisible para ellas por construcción. Lo detectó Sara mirando un panel de
negocio, y eso no es un fracaso del sistema: es la razón por la que existe mercadofresco-negocio.
(b) Los primeros 15 minutos. Contener: si franja_entrega está tras una bandera de AppConfig,
apagarla —los clientes vuelven al comportamiento anterior en segundos y el incidente termina ahí—. Si no,
deshabilitar la transición hacia producción para que nadie despliegue encima. Confirmar: comprobar en
CloudWatch que la caída de tarde es real y no un problema del panel, y mirar los registros de la API
filtrando por peticiones con franja_entrega=tarde —lo más probable es un 400 o un 422 sistemático—.
Acotar: ¿fallan todos los pedidos de tarde o solo algunos? ¿desde las 17:00 exactas? Que empiece a las
17:00 y no a las 11:00 es la pista principal: sugiere una condición horaria, por ejemplo una validación
que rechaza la franja tarde cuando ya ha pasado su hora de corte, con la zona horaria mal calculada.
Avisar: al equipo y a Sara, con lo que se sabe y lo que no.
(c) Hacia atrás, a la 1.5.2, si la bandera no existe o no resuelve. Y se puede hacer sin miedo
precisamente porque la migración fue de expansión: la columna franja_entrega es NULLABLE, la 1.5.2
no la conoce y la ignora, así que revertir el código deja el sistema exactamente como estaba antes del
martes. Los pedidos de las últimas horas conservan su franja en la columna, que se recuperará al desplegar
la versión corregida. Si la migración hubiera sido de contracción, la respuesta sería la contraria —hacia
adelante, corrigiendo— y con la tienda degradada mientras tanto: la fase de expansión es lo que convierte
esta decisión en fácil.
(d) Los pedidos de esas dos horas y media. No están en la DLQ, porque probablemente ni llegaron a
crearse: son clientes que intentaron elegir tarde, vieron un error y se fueron. Es una pérdida de
ventas, no de datos, y hay que tratarla como tal: estimar el volumen comparando con la misma franja de los
martes anteriores, y decidir con Sara si procede alguna acción comercial. Si además hubiera pedidos creados
con la franja mal asignada, se identifican por rango temporal y se corrigen con un script revisado, nunca
con un UPDATE improvisado en producción. Y si hay eventos que no se publicaron, se recuperan del archivo
de EventBridge con FilterArns.
(e) Tres controles del post-mortem. Una alarma por dimensión, no solo por agregado: una métrica
PedidosConfirmados con dimensión FranjaEntrega y una alarma que salte si cualquier franja cae a cero
durante 15 minutos en horario comercial. Es barata y habría avisado a las 17:15 en lugar de a las 19:30.
Una prueba de humo con reloj: los casos que dependen de la hora hay que probarlos a horas distintas, así
que una ejecución sintética cada hora con las tres franjas, o pruebas unitarias con el reloj simulado
cubriendo los bordes —y muy en particular la zona horaria, que aquí es la sospechosa—. Y extender la
puerta de calidad con una segunda evaluación diferida: una comprobación automática a las 4 y a las 12
horas del despliegue que compare las métricas por dimensión con el mismo periodo de la semana anterior y
avise si algo se ha movido. No bloquea el pipeline —ya terminó— pero convierte «lo vio Sara por casualidad»
en «avisó el sistema».
Solución 3
Frecuencia de despliegue. MercadoFresco está en 19 al mes, unos 4,4 por semana: categoría alta. La élite es bajo demanda, varias veces al día. El cambio: quitar la aprobación manual —que es lo que impone un ritmo diario— y agrupar menos. El riesgo: quitarla sin que la puerta de calidad esté madura cambia un control por una hipótesis, y las condiciones de salida que Marta escribió en 08-04 existen precisamente para eso.
Tiempo de entrega. 3 h 20 min es alta; la élite es menos de una hora. Y el diagnóstico está servido: 2 h 40 min de las 3 h 20 son espera humana, así que el pipeline técnico ya tarda 40 minutos y está en rango élite. El cambio no es optimizar la construcción: es la aprobación. Dos medidas graduales antes de quitarla del todo: aprobación desde Slack con AWS Chatbot, que baja la espera de horas a minutos porque Marta no necesita estar delante del ordenador, y un suplente designado. El riesgo de la primera es prácticamente nulo, y por eso es la que hay que hacer ya.
Tasa de fallo de cambios. 2 de 19 es un 10,5 %: categoría media-alta; la élite está por debajo del 15 %, así que técnicamente ya está. Pero el número engaña sin mirar los casos: uno de los dos fallos fue el de las 19:30, que no lo detectó ninguna puerta. El cambio útil no es bajar el porcentaje, es mejorar la detección con los tres controles del ejercicio 2. El riesgo de perseguir el número por sí mismo es el de siempre: se dejan de desplegar cambios pequeños para no arriesgar la estadística.
Tiempo de restauración. Mediana de 4 minutos, que es élite, pero con un caso de 95 minutos. Aquí la mediana miente: lo que duele es la cola. El cambio: banderas de funcionalidad en todo cambio de comportamiento visible, que convierten la restauración en segundos sin desplegar, y la alarma por dimensión que reduce el tiempo de detección, que es donde estaban 90 de esos 95 minutos.
Primero, la aprobación desde Slack. Es la que más impacto tiene —2 h 40 min de 3 h 20—, la más barata —una integración de una tarde—, la de menor riesgo —no elimina ningún control, solo lo hace más accesible— y la que además mejora indirectamente la frecuencia, porque la espera de la aprobación es lo que empuja a agrupar cambios. Empezar por quitar la aprobación sería atacar la misma métrica con mucho más riesgo y sin haber cumplido las condiciones que el propio equipo se puso.
Conclusión
Un cambio de Luis ha recorrido el camino completo. Empezó como una petición de Sara sobre el 31 % de
incidencias de reparto y terminó en producción tres horas después, tocando el código de la tienda, un
consumidor de cola-mercadofresco-pedidos, el esquema de Aurora y el contrato de PedidoConfirmado, sin
que nadie escribiera un comando. La rama duró dos días, la PR se revisó con la construcción en verde, el
artefacto se construyó una vez y ese mismo objeto recorrió los tres entornos, Marta aprobó en cuarenta
segundos con la información delante, y la puerta de calidad confirmó con métricas reales que la tienda
seguía sana.
Lo que hace que esto funcione no son los servicios, son las dos disciplinas que esta lección ha puesto en
práctica. La primera es la compatibilidad: la migración en fase de expansión con la columna NULLABLE,
sin NOT NULL, sin DEFAULT y sin índice, que convierte un cambio irreversible en uno inocuo; y el evento
versionado con un atributo opcional, con las dos pruebas de contrato que casi nadie escribe —el
consumidor viejo con el evento nuevo y el consumidor nuevo con el evento viejo—, porque durante un
despliegue gradual las dos situaciones existen a la vez. La segunda es el orden: primero el que lee,
después el que escribe, declarado como runOrder en el pipeline y no como algo que alguien debe recordar.
Ahí queda cerrado el incidente que el módulo 7 dejó abierto.
Tienes la pirámide de pruebas con sus niveles bloqueantes y el que casi nadie tiene —las de contrato, quince segundos que son lo único que detecta que un consumidor ajeno se va a romper—; la configuración por entorno en Parameter Store y los secretos en Secrets Manager, con la regla de que un valor puesto a mano en una instancia desaparece en el siguiente blue/green y vuelve como un fallo indiagnosticable; y el runbook de reversión con su decisión más difícil, la dirección: atrás si solo cambió código o hubo expansión, adelante si hubo contracción.
Y tienes los números. Frecuencia de despliegue de 1,2 a 4,8 por semana. Tiempo de entrega de 6 días a 3 horas. Tasa de fallo del 23 % al 7 %. Tiempo de restauración de 47 minutos a 4. Con las tres lecturas que los acompañan: que la frecuencia sube porque el coste marginal de desplegar baja, que la restauración es la métrica que de verdad cambia la naturaleza del riesgo, y que un 0 % de fallos sería señal de estar desplegando demasiado poco —el objetivo no es no fallar, es que fallar cueste cuatro minutos y no cuarenta y siete—. Más las banderas de funcionalidad con AppConfig, que separan desplegar de publicar y tienen su propia deuda si nadie las retira, y la conversión de «no desplegamos los viernes» en una ventana justificada con datos: cinco veces más pedidos en juego y un tercio del equipo disponible. Una decisión de negocio, no de fe.
El cuarto problema del curso queda resuelto, y por unos 28 dólares al mes. MercadoFresco tiene un código gobernado, una construcción que verifica, un despliegue que se revierte solo y un pipeline que lo encadena todo.
Pero hay una asimetría difícil de justificar y que esta lección ha dejado a la vista dos veces. El código
de la aplicación está gobernado; la infraestructura no. La VPC se creó con create-vpc en un terminal.
Las colas con create-queue. Las reglas de EventBridge subiendo un JSON. Las alarmas, los grupos de
seguridad, los grupos de destino del ALB, las tablas de DynamoDB: todo existe porque alguien escribió un
comando algún día, y en ningún sitio hay un registro de por qué. Nadie puede recrear el entorno de
MercadoFresco desde cero. Nadie puede garantizar que preproducción tenga la misma configuración que
producción —de hecho no la tiene, y la diferencia se descubre en cada incidente—. Y el propio pipeline que
existe para que nadie despliegue a mano se creó a mano, con un JSON en el portátil de Luis. Los tres
entornos comparten además la cuenta 111122223333, así que el radio de explosión no está acotado, las
cuotas se comparten y la factura no se separa de verdad.
En el módulo 9, «Infraestructura como código y gobierno de cuentas», atacamos el quinto y último problema del curso. 09-01, «AWS CloudFormation», trae las plantillas declarativas, las pilas, los conjuntos de cambios que dicen qué va a pasar antes de que pase y la reversión de infraestructura. 09-02, «AWS CDK», permite definir esa misma infraestructura con código de verdad, con abstracciones y pruebas. 09-03, «Elastic Beanstalk», muestra la alternativa gestionada y cuándo compensa. Y 09-04, «AWS Organizations», resuelve la separación seria de entornos con cuentas distintas, políticas de control de servicios y facturación consolidada. Al final de ese módulo, todo lo que hemos construido en ocho módulos podrá recrearse desde cero con un comando.
Curso de AWS
Módulo 1: Introducción a AWS
- ¿Qué es AWS?
- Configuración de tu cuenta de AWS
- Infraestructura global de AWS
- Consola de administración de AWS
- AWS CLI y SDKs
Módulo 2: Servicios principales de AWS
Módulo 3: Redes y entrega de contenido
- Amazon VPC
- Grupos de seguridad y listas de control de acceso
- Elastic Load Balancing
- Amazon CloudFront
- Route 53
Módulo 4: Seguridad e identidad
- AWS Identity and Access Management (IAM)
- AWS Key Management Service (KMS)
- Secrets Manager y Parameter Store
- AWS Shield
- AWS WAF
Módulo 5: Monitorización y gestión
- Amazon CloudWatch
- AWS X-Ray y trazabilidad distribuida
- AWS CloudTrail
- AWS Config
- AWS Trusted Advisor
Módulo 6: Bases de datos
- Cómo elegir la base de datos adecuada
- Amazon DynamoDB
- Amazon Aurora
- Amazon Redshift
- Amazon ElastiCache
Módulo 7: Integración de aplicaciones
- Amazon SQS
- Amazon SNS
- Amazon EventBridge
- AWS Step Functions
- Patrones de integración: idempotencia, reintentos y colas de mensajes fallidos
