# Database — Fundamental Skill

Skill para diseño de bases de datos, migraciones, queries eficientes y transacciones. Usar al diseñar schemas, escribir queries, o planificar migraciones.

---

## Schema Design

### Naming conventions
- Tablas en plural, snake_case: ✅ user_orders ❌ UserOrder
- Primary keys: id UUID o bigint: ✅ id UUID DEFAULT gen_random_uuid()
- Foreign keys: tabla_singular_id: ✅ user_id REFERENCES users(id)
- Timestamps en todas las tablas: created_at, updated_at
- Índices: idx_tabla_columna: ✅ CREATE INDEX idx_orders_user_id ON orders(user_id)
- Booleanos: is_ o has_: ✅ is_active ❌ active
- Enum como string con CHECK: ✅ status TEXT CHECK (status IN (...)) ❌ ENUM type

### Tipos de datos
- UUID: Primary keys, external IDs. ❌ IDs autoincrementales
- TEXT/VARCHAR(n): Strings con tamaño predecible
- TIMESTAMPTZ: Siempre. ❌ TIMESTAMP sin zona horaria
- NUMERIC/DECIMAL: Dinero, porcentajes. ❌ FLOAT para dinero
- JSONB: Datos semi-estructurados. ❌ JSON (sin B, no indexable)
- BOOLEAN: Flags binarios. ❌ TINYINT para booleanos

---

## Migraciones

### Reglas de oro
1. Cada migración es un archivo con timestamp. Nunca edites una migración ya aplicada en producción.
2. Forward-only en producción. Para revertir, creá nueva migración que deshaga el cambio.
3. Probá en staging antes de producción. ALTER TABLE sin probar = ruleta rusa.
4. Nunca mezcles cambios de schema con cambios de datos en la misma migración.
5. Incluí seeds para desarrollo local. Levantar el proyecto debe ser un solo comando.
6. Migraciones deben ser reversibles (método down) para desarrollo.

---

## Query Patterns

### N+1 Queries
- Problema: Una query para N registros + N queries más para datos relacionados
- Solución: Eager loading (JOIN o INCLUDE)

### SELECT *
- Problema: Traés todas las columnas, desperdiciás ancho de banda
- Solución: Seleccioná solo las columnas necesarias. Especialmente importante con JSONB/TEXT

### Missing index
- Problema: Full table scan, exponencial con el crecimiento
- Solución: Índices en WHERE, JOIN, ORDER BY. Basado en queries reales, no suposiciones

### Query en loop
- Problema: Query dentro de un for loop
- Solución: WHERE IN con arrays o batch inserts

### Sin paginación
- Problema: SELECT sin LIMIT/OFFSET en tabla grande
- Solución: Siempre paginá. Cursor-based para datasets que cambian frecuentemente

### Índices — reglas
1. Basado en queries reales, monitoreá slow queries
2. Un índice por query frecuente. Demasiados ralentizan escrituras
3. Índices compuestos: el orden importa (a, b) ≠ (b, a)
4. Partial indexes para WHERE fijo
5. EXPLAIN ANALYZE antes de deployar

---

## Transacciones & Locks

### Niveles de isolation
- READ COMMITTED (default PostgreSQL): Suficiente para 90% de casos
- REPEATABLE READ: Útil para reportes que necesitan consistencia
- SERIALIZABLE: Máxima seguridad, mínimo rendimiento

### Reglas de oro
1. Abrí transacciones solo cuando sea necesario
2. Mantené las transacciones cortas. Nada de HTTP/emails dentro
3. Actualizaciones atómicas de múltiples tablas → transacción
4. Deadlocks: orden consistente de updates lo previene
5. En producción: manejá errores de transacción con retry + backoff
