63 puntos por GN⁺ 2025-04-01 | 4 comentarios | Compartir por WhatsApp
  • Un artículo que recopila patrones prácticos para usar Postgres de forma más productiva y segura
  • Cada patrón es pequeño, pero al acumularse genera una gran diferencia

Uso de claves primarias UUID

  • Como los UUID son aleatorios, tienen desventajas en términos de ordenamiento y rendimiento de índices
  • Ocupan más espacio que los ID numéricos
  • Pero tienen las siguientes ventajas
    • Se pueden generar UUID sin conectarse a la BD
    • Se pueden exponer externamente de forma segura
  • Se puede usar gen_random_uuid() para generar automáticamente UUID como clave primaria

Agregar siempre los campos created_at y updated_at

  • Al depurar, es muy útil saber cuándo se creó y modificó un registro
  • updated_at puede configurarse para actualizarse automáticamente mediante un trigger
  • La función se crea una sola vez, y el trigger debe aplicarse a cada tabla

Configurar on update/delete restrict en claves foráneas

  • Al definir restricciones de clave foránea, conviene usar siempre on update restrict on delete restrict
  • Esto evita que se produzcan eliminaciones en cascada por error al borrar datos
  • El almacenamiento es barato, pero recuperar datos es muy difícil, por lo que conviene ser conservador

Se recomienda usar esquemas

  • El esquema predeterminado es public, pero cuando la aplicación crece conviene separarla en esquemas propios
  • Los esquemas funcionan como namespaces y permiten joins incluso entre esquemas distintos
  • Cuantas más tablas haya, más conviene aprovechar los esquemas para mejorar legibilidad y mantenimiento

Usar el patrón de tablas enum

  • En lugar de usar el tipo enum de PostgreSQL o un check constraint, usar tablas enum resulta más flexible
  • Si los valores enum se administran en una tabla separada, es fácil agregar metadatos o extenderlos
  • Las restricciones se mantienen al referenciar los valores de la tabla enum mediante claves foráneas

Nombrar las tablas en singular

  • Es preferible nombrar las tablas en singular y no en plural
  • Al escribir consultas, el singular es más claro, y el plural puede causar confusión semántica o posesiva

Nombrar las tablas de unión de forma mecánica

  • Para relaciones muchos a muchos, es más seguro y claro nombrar la tabla de unión concatenando los nombres de ambas tablas
  • Ejemplo: person_pet
  • Agregar un índice único sobre la combinación para evitar duplicados

Usar soft delete en lugar de borrar

  • En vez de eliminar realmente los datos, conviene usar un campo timestamp como revoked_at que indique cuándo se eliminaron
  • Esto permite rastrear no solo si se eliminó, sino también cuándo ocurrió
  • Un timestamp aporta más información que un valor booleano

Representar el estado (Status) con una tabla de logs

  • En lugar de expresar el estado con una sola columna, se guarda el historial de cambios de estado en una tabla separada
  • El momento en que ocurre el estado se indica explícitamente con la columna valid_at
  • Para consultar rápidamente el estado más reciente, se configuran un flag latest y un índice único + trigger
  • Esto es útil en procesamiento de eventos asíncronos o situaciones donde el orden puede mezclarse

Agregar system_id a filas especiales

  • Además de las tablas enum, a veces se necesitan ciertas "filas del sistema"
  • Se agrega un campo de texto system_id nullable y se configura un índice único
  • system_id permite consultar con claridad una fila específica

Usar las vistas (View) al mínimo

  • Las vistas son útiles para abstraer consultas complejas, pero son difíciles de mantener
    • Si se elimina una columna, hay que volver a crear la vista
    • Si se crean vistas sobre vistas, aparecen problemas de rendimiento y legibilidad
  • Conviene usarlas con cuidado y solo cuando sea necesario

Aprovechar activamente las consultas JSON

  • Postgres es muy potente no solo para almacenar JSON, sino también para devolver JSON desde consultas
  • Puede devolver relaciones anidadas en formato JSON con una sola consulta
  • Permite traer todos los datos necesarios de una vez sin el problema de N+1
  • Desventajas: pérdida de información de tipos y necesidad de cargar todos los datos en memoria de una sola vez
  • Las ventajas en rendimiento o estructura son mayores

4 comentarios

 
jhj0517 2025-04-01

> Nombrar las tablas de unión de forma mecánica

Creo que está bueno que exista una regla así para ponerles nombre~

 
halfenif 2025-04-01

Si consideras UUID7, ¿no sería posible ordenar cronológicamente?

 
winterjung 2025-04-01

Parece que también valdría la pena echarle un vistazo al artículo sobre usar UUID como clave primaria en PostgreSQL.

 
t7vonn 2025-04-01

Qué buena forma de agregar un timestamp al hacer soft delete.
Si usas UUID como clave primaria, no se puede ordenar por tiempo, así que también podría ser buena idea usar un snowflake ID o ULID. Aunque en ese caso cada servidor tendría que tener su propio sequence number.