Módulo 03Trimestre 1 · Fundamentos8 puntos del póster

Fundamentos de datos

Inicia sesión para guardar tu progreso en todos tus dispositivos. Mientras tanto, se guarda en este navegador.

Por qué importa

El código se puede volver a desplegar. Los datos corruptos no se pueden des-corromper. La base de datos es la parte del sistema en la que menos te puedes permitir equivocarte, y la que las herramientas de IA tratan con más ligereza.

Resultados de aprendizaje

  1. Modelar los datos con claves, restricciones y multi-tenancy desde el primer día.
  2. Encontrar y corregir consultas lentas con EXPLAIN e índices.
  3. Eliminar las consultas N+1.
  4. Configurar connection pooling para serverless.
  5. Usar transacciones y bloqueos para mantener los datos correctos bajo concurrencia.
  6. Publicar cambios de esquema sin tiempo de inactividad.

Lecciones

3.1 Modelar para la corrección

  • Deja que la base de datos haga cumplir las reglas: NOT NULL, UNIQUE, CHECK, claves foráneas. Las verificaciones en la aplicación sufren condiciones de carrera; las restricciones no.
  • Dinero: enteros en la unidad menor (centavos) o numeric, nunca float.
  • Tiempo: guarda en UTC (timestamptz) y convierte al mostrar.
  • IDs: UUIDv7 o similar para los IDs públicos; nunca expongas IDs secuenciales que permitan a un atacante enumerar registros.
  • Multi-tenancy: cada fila pertenece a un org_id / user_id, y cada consulta se filtra por él. Hazlo cumplir en la base de datos con Row-Level Security (RLS de Postgres), no solo en el código de la app.

3.2 Indexación y optimización de consultas (25, 26)

  • Un índice es una estructura de búsqueda ordenada; sin él, la base de datos recorre todas las filas.
  • Indexa las columnas por las que filtras, haces join y ordenas. En un índice compuesto el orden importa (primero las columnas de igualdad, después las de rango).
  • Cada índice ralentiza las escrituras y ocupa espacio; no lo indexes todo.
  • Lee EXPLAIN ANALYZE: busca sequential scans en tablas grandes, estimaciones de filas altas y ordenamientos que se derraman a disco.
  • Activa el registro de consultas lentas (pg_stat_statements) y revísalo cada semana.

3.3 Consultas N+1 (27)

// N+1: 1 query for orders + 1 per order for its customer
const orders = await db.order.findMany();
for (const o of orders) o.customer = await db.customer.findUnique({ where: { id: o.customerId } });

// Fixed: 2 queries total (or a join)
const orders = await db.order.findMany({ include: { customer: true } });

Detéctalas registrando las consultas en las pruebas: verifica el número de consultas por solicitud.

3.4 Connection pooling (28)

Cada conexión de Postgres consume memoria; las bases de datos limitan las conexiones (a menudo a unas 100). Las funciones serverless abren una por invocación y las agotan bajo carga. Usa un pooler (PgBouncer, Supavisor, Neon pooler, Prisma Accelerate) en modo transacción para serverless.

3.5 Transacciones y bloqueos (36, 37)

  • Envuelve las escrituras de varios pasos en una transacción: todo o nada.
  • Bloqueo optimista: columna version; UPDATE ... SET version = version + 1 WHERE id = $1 AND version = $2; si se actualizan cero filas, alguien más lo cambió, así que reintenta o informa.
  • Bloqueo pesimista: SELECT ... FOR UPDATE bloquea la fila dentro de una transacción. Úsalo para recursos muy disputados, como saldos e inventario.
  • Bug de ejemplo: dos clics simultáneos en "aprobar préstamo" leen ambos status = pending y ambos desembolsan. Se corrige con una actualización condicional (WHERE status = 'pending') y comprobando el número de filas afectadas.

3.6 Migraciones y versionado de esquema (81, 82)

  • Cada cambio de esquema es un archivo de migración versionado en git, aplicado por la CI, nunca a mano desde un dashboard.
  • Expand and contract para cero tiempo de inactividad: (1) añade la columna nueva como nullable, (2) despliega código que escriba en ambas, (3) rellena los datos por lotes, (4) cambia las lecturas, (5) elimina la columna vieja en una versión posterior.
  • Operaciones peligrosas: añadir NOT NULL sin valor por defecto en tablas grandes, renombrar columnas en uso, crear índices sin CONCURRENTLY.
  • Nunca dejes que un agente de IA ejecute migraciones directamente contra producción.

Lab 3: Rompe y arregla tu capa de datos

  1. Carga 1 millón de filas. Encuentra las tres consultas más lentas; corrígelas con índices; muestra el EXPLAIN de antes y después.
  2. Añade una aserción del número de consultas a una prueba de la API; encuentra y corrige un N+1.
  3. Escribe una prueba que lance 20 solicitudes concurrentes de "canjear cupón"; demuestra que el cupón se canjea exactamente una vez.
  4. Activa RLS en todas las tablas; escribe una prueba que demuestre que el usuario A no puede leer las filas del usuario B usando la anon key pública.
  5. Haz un renombrado de columna expand-and-contract sin tiempo de inactividad.

Definition of Done

Pack de prompts para la IA

  • "Enumera todas las consultas que ejecuta este endpoint y marca las que estén dentro de un bucle."
  • "Escribe las políticas de RLS para estas tablas de modo que los usuarios solo accedan a filas de las organizaciones a las que pertenecen. Después escribe pruebas que intenten saltárselas."
  • "Planifica este cambio de esquema como una migración expand-and-contract repartida en despliegues separados."